Pros and Cons of using e.stopPropagation() to prevent event bubbling

tim peterson picture tim peterson · May 11, 2013 · Viewed 7.8k times · Source

Many people have explained that e.stopPropagation() prevents event bubbling. However, I'm having a hard time finding why one would want or want to prevent event bubbling in the first place.

On my site I have many elements which are called like this:

$(document.body).on('click', ".clickable", function(e){ 
   //e.stopPropagation();
  //do something, for example show a pop-up or click a link   
});

<body>
  <p>outside stuff</p>
  <button type="button" class='clickable'>
    <img src='/icon.jpg'> Do Something

  </button>
</body>

I was thinking to add e.stopPropagation() because I want to change the event handler to 'touch' from 'click' using this awesome touch library, Hammer.js.. This would allow for clicking to happen normally on desktop and for touch events on mobile devices.

The problem with this (please correct me if I'm wrong) is that scrolling on touch devices slows to a halt.

Is this where e.stopPropgation() is useful? Such that whenever one touches the screen document.body-event bubbling is NOT happening every time?

Answer

jfriend00 picture jfriend00 · May 11, 2013

There are several ways you can handle events in javascript/jQuery. Two of them are:

  1. You can use a direct event handler on the object.
  2. You can use delegated event handling where you handle a propagated event on a parent.

If you are using a direct event handler on the object and there are no delegated event handlers configured in the page, then there is no reason for e.stopPropagation().

But, if you have delegated event handlers that are using propagation, you sometimes want to make sure that a higher level delegated event handler does not fire on the current event.

In your particular example:

$(document.body).on('click', "a.ajaxLink", function(e){ 

This is a delegated event handler. It is looking for any click event that propagates up to the document.body object, yet originated on an a.ajaxLink object. Here, there is little advantage to e.stopPropagation() because the event is almost entirely propagated already (it will also go up to the document, but unless you also have a handler for click on the document object, then there's no reason to e.stopPropagation() in this handler.

Where it would make a lot of sense would be when you have both a top level delegated event handler (like the one in your example) and you have lower level event handlers (either directly on the objects or using delegated event handling, but at a level below the document.body object. In that case, if you only want the lower level event handler to get the event, then you would call e.stopPropagation() in it's handler so that the document.body handler never saw the event.

$("a.ajaxLink").click(function(e) {
    if (some condition) {
        // do something specific to this condition
        code here
        // stop propagation so the default behavior for click in document.body does not fire
        e.stopPropagation();
    }
})

Note: Using return false from a jQuery event handler triggers both e.stopPropagation() and e.preventDefault(). But, if you are in a delegated event handler, the e.preventDefault() doesn't do anything because the default behavior (if there was one) has already fired when the target object first saw the event. Default behaviors happen before event propagation so e.preventDefault() only works in event handlers directly on the target object.


There is no noticeable performance degradation because you allow an event to bubble up because these are user level events and they just don't occur fast enough to matter, not is the bubbling particularly slow when there are no handlers on all the intervening objects. The system already special cases some events like mousemove that can happen rapidly to solve that issue. If you have a giant project with hundreds or thousands of event handlers, there are cases where using delegated event handling is more efficient and there are cases where direct event handlers on the actual target objects are more efficient. But, except in giant scenarios, the performance difference is probably not noticeable.

Here's an example where bubbling/delegation is more efficient. You have a giant table with thousands of rows and each row has two buttons in it (add/delete say). With delegated event handling, you can handle all the buttons in two simple event handlers that you attach to the table object (a common parent of the buttons). This will be much quicker to install the event handlers rather than installing several thousand event handlers directly on each and every button. These delegated event handlers will also automatically work on newly created rows/objects in the table. This is the ideal scenario for event bubbling/delegated event handlers. Note, in this scenario, there is no reason to stop propagation/bubbling.

Here's an example of where delegated event handlers are very inefficient. Supposed you have a good-sized web page with hundreds of objects and event handlers. You could make every one of the event handlers be a delegated event handler attached to the document object. But, here's what happens. A click happens. There's no event handler on the actual object so it bubbles up. Eventually, it gets to the document object. The document object has hundreds of event handlers. The event processing engine (jQuery in this case) has to look through every one of those event handlers and compare the selector in the delegated event handler for each one with the original event target to see if they match. Some of these comparisons are not fast as they can be full-blown CSS selectors. It has to do that for hundreds of delegated events. This is bad for performance. This is exactly why .live() in jQuery was deprecated because it worked this way. Instead, delegated event handlers should be placed as close to the target object as possible (the closest parent that is practical given the circumstances). And, when there is no need for a delegated event handler, handlers should be put on the actual target object as this is the most efficient at runtime.


Back to your original question. There is no time that you want bubbling turned off generally. As I described earlier in my answer, there are specific instances where an event handler further out on the tree wants to handle the event and stop any delegated event handlers higher up in the DOM tree from processing this event. That is a time to e.stopPropatation().


Here are several other relevant posts with useful info on this topic (as it has been widely discussed before):

Why not take Javascript event delegation to the extreme?

Should all jquery events be bound to $(document)?

Does jQuery.on() work for elements that are added after the event handler is created?

jQuery on() and stopPropagation()

Best practice to avoid memory or performance issues related to binding a large number of DOM objects to a click event

jQuery .live() vs .on() method for adding a click event after loading dynamic html