Pros and cons of using e.stopPropagation () to prevent event bubbles

Many people have explained that e.stopPropagation() prevents event bubbles. However, it is difficult for me to find why it is necessary or wants to prevent a surge in events in the first place.

On my site I have many elements that 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 of adding e.stopPropagation() because I want to change the event handler to 'touch' from 'click' using this amazing touch library, Hammer.js. . This will allow you to click normally on the 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 down.

This is where e.stopPropgation() is useful? So every time someone touches the document.body -event bubbling screen, does NOT happen every time?

+6
source share
3 answers

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

  • You can use the direct event handler on the object.
  • You can use delegated event processing, where you handle a common event for the parent.

If you use the direct event handler on the object and the handlers of the processed events are not configured on the page, then there is no reason for e.stopPropagation() .

But if you have delegated event handlers that use distribution, you sometimes want to make sure that the higher level handler does not fire the current event.

In your specific example:

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

This is a delegated event handler. It searches for any click event that propagates to the document.body object, but a.ajaxLink from the a.ajaxLink object. There are few advantages for e.stopPropagation() , because the event is almost completely distributed (it will also correspond to document , but if you also have a handler for e.stopPropagation() document object, then there is no reason for e.stopPropagation() in this handler.

If this makes sense, when you have a delegated top-level event handler (for example, the one in your example) and you have lower-level event handlers (either directly on objects or using delegated event processing, but at the level below the document.body e.stopPropagation() this case, if you want the lower-level event handler to receive the event, you would e.stopPropagation() in its handler so that the document.body handler would never see 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 the jQuery event handler fires both e.stopPropagation() and e.preventDefault() . But, if you are in a delegated event handler, e.preventDefault() does nothing, because the default behavior (if any) is already running when the target first saw the event. The default behavior occurs before the event propagates, so e.preventDefault() only works in event handlers directly on the target.


There is no noticeable performance degradation because you allow the event to bubble up, because these are user-level events, and they simply do not happen fast enough to make a difference, and it is not bubbling especially slowly when there are all intermediate objects. There are already special cases in the system where some events like mousemove can quickly occur to solve this problem. If you have a giant project with hundreds or thousands of event handlers, there are times when using delegated event processing is more efficient, and there are times when direct event handlers on real targets are more efficient. But apart from the gigantic scenarios, the performance difference is probably not noticeable.

Here is an example where bubbling / delegation is more efficient. You have a giant table with thousands of rows, and each row has two buttons (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 (the common parent of the buttons). It will be much faster to install event handlers, rather than installing several thousand event handlers directly on each button. These delegated event handlers will also automatically work with the newly created rows / objects in the table. This is an ideal scenario for event handlers / event delegation. Please note that in this case there is no reason to stop the distribution / sparging.

Here's an example of where delegated event handlers are very inefficient. Suppose you have a good sized web page with hundreds of objects and event handlers. You can make each of the event handlers a delegated event handler attached to the document object. But here is what happens. Clicking occurs. There is no event handler on the actual object to bubble. In the end, it falls into the document object. A document object contains hundreds of event handlers. The event processing engine (jQuery in this case) should look at each of these event handlers and compare the selector in the delegated event handler for each of them with the original purpose of the event to see if they match. Some of these comparisons are not fast because they can be full-blown CSS selectors. He has to do this for hundreds of delegated events. This is bad for performance. This is why .live() in jQuery is deprecated because it worked that way. Instead, delegated event handlers should be located as close as possible to the target (the closest parent, which is practical given the circumstances). And, when there is no need for a delegated event handler, the handlers must be placed in the actual target, as this is most efficient at run time.


Return to the original question. There is no time when you want the bubbles to turn off altogether. As I described earlier in my answer, there are specific cases where an event handler located further down the tree wants to process the event and stop any delegated event handlers above in the DOM tree from processing this event. This is the time of e.stopPropatation() .


Here are a few other relevant posts with useful information on this topic (as was widely discussed earlier):

Why not take a Javascript event as a last resort?

Should all jquery events bind to $ (document)?

Does jQuery.on () work for items added after creating an event handler?

jQuery on () and stopPropagation ()

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

jQuery.live () vs .on () method to add click event after loading dynamic html

+11
source

Imagine you have a button and you want to handle the click event:

 <button> <img src="icon" /> <span>Text</span> </button> 

Without distributing the event, clicking on the image or text will not bind the click event handler to the button, since the event will never leave the <img> or <span> elements.


The case where you don't want to propagate events is where the nested elements have their own event handlers. Here is one example:

 <button> <img src="icon" /> <span>Text</span> <span class="arrow">&darr;</span> </button> 

If you do not stop distributing the event using the .arrow event .arrow , it will also call the button's event handler.

+2
source

Do not use stopPropagation() if possible.

The two benefits of using stopPropagation() are:

  • Easier to write code (independent event functions)
  • Performance

Although this feature seems useful, it can be seen as a bad coding style, especially if you do not have full control over the code (for example, because you use a third-party library). stopPropagation() is an all-nothing concept. It does not allow precise flow control. If an element between two other nested elements stops the event from spreading, none of the parent elements will receive it, although situations may arise when they should receive it.

A more elegant (and not so complicated) way to solve this problem is to always allow the propagation of the event, never calling stopPropagation() at all. Elements that define their own events, which should not be automatically executed from the child, can use target and currentTarget to check where the initial event comes from, and to perform their own functions of events only in situations when it is required.

In the following example, there are 3 nested DIVs. Clicking on the lowest (blue) DIV will propagate the onClick event throughout the DOM structure, but without raising the onClick event of the green DIV, which is between:

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> div { min-width: 100px; min-height: 50px; display: inline-block; padding: 2em; } #div1 { background: red; } #div2 { background: green; } #div3 { background: blue; } #info { display: block; white-space: pre; } </style> <script> window.addEventListener('load', function() { // #div1, #div3 document.querySelector('#div1').addEventListener('click', function(e) { if (e.target == e.currentTarget || e.target == document.querySelector('#div3')) { document.querySelector('#info').textContent += 'I am #1 or #3\n'; } }); // #div2 document.querySelector('#div2').addEventListener('click', function(e) { if (e.currentTarget == e.target) { document.querySelector('#info').textContent += 'I am #2\n'; } }); }); </script> </head> <body> <div id="div1"> <div id="div2"> <div id="div3"></div> </div> </div> <div id="info"></div> </body> </html> 

Thus, calling stopPropagation() on the onClick event function for DIV # 3 is not required to prevent the onClick event from firing if DIV # 2.

Also note that the structure of the document is as follows:

 document document.documentElement document.body ... 

If the event propagation is not stopped, it will reach the document object. event.currentTarget will then be document , and event.target will be either document.documentElement , document.body , or any subitem under the <body> element.

So, considering that you have the following code:

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> html { background: #009688; } body { background: #bbb; } </style> </head> <body> <div>Hello world</div> <div>Hello world</div> <div>Hello world</div> <div>Hello world</div> <div>Hello world</div> <div>Hello world</div> </body> </html> 

Here's what it looks like and where the different parts of the document are: Simple page

Gray is an area of ​​the body. Green is the actual element document (the largest part of the style). And behind it is an invisible document object.

If you want to use the functions of the events that will be executed only for the elements under your finger / mouse cursor, you can use the following code (example for the onClick event):

 elm.addEventListener('click', function(e) { if ( ( (e.currentTarget == document) && (e.target == document.documentElement || e.target == document.body) ) || (e.currentTarget == e.target) ) { // ... } }); 

It works with document.documentElement , document.body or any element of the document without having to call stopPropagation() .

0
source

Source: https://habr.com/ru/post/944746/


All Articles