Event bubbling mechanism

Let’s look at a piece of code first

<! DOCTYPE html> <html> <head> <title>Event Capture and Propagation</title> </head> <body> <div id="parent" style="width:120px; height:120px; background:#090; padding:60px"> <div id="child" style="width:120px; height:120px; background:#0CC"></div> </div> <script type="text/javascript"> function alertName(e) { alert(this.id); } document.getElementById('child').onclick = alertName; document.getElementById('parent').onclick = alertName; </script> </body> </html>Copy the code

We bind the alertName(e) method to the onclick event of both the outer and inner divs. When we click on the outer green part, the event of the outer div responds; Let’s click on the inner blue section, and the events of the inner div will respond first, and then the events of the outer div will respond again.

So it’s easy to conclude that the browser finds the currently clicked element and responds if an onclick event is registered on that element. The browser then continues to look for the parent element of the clicked element, which responds if it is also registered with the onClick event, and continues to look for the upper layer. This is the JS event bubbling mechanism. Let’s try a different browser. The current versions of Chrome, Safari, Firefox and Internet Explorer all have the same results.

Event processing model

There are actually two models of event processing. One is called the bubble type, which is the example we saw above, and the order is from inside out “div” -> “body” -> “HTML” -> “document” -> “window”. And then there’s the catch type, which is the reverse order. IE is bubble-enabled, and historically Netscape was capture-enabled. What about our mainstream W3C standards? The answer is yes, the default is bubbling. How do you understand that? Let’s take a look at the W3C DOM event flow:

When an event occurs, the capture phase begins, propagating the event from the outermost layer inward, all the way to the target element that triggered the event (the div Child in the example above). It then enters the bubble phase, starting with the element that triggered the event and propagating outward to the outermost element. So why didn’t we see the event capture phase in the previous example? Here we have another concept.

Add event binding

The above example sets the onclick event property to the alertName(e) method, which is generally not recommended because it overrides other functions previously bound. We recommend the addEventListener method. We make the following changes to the code that binds the events in the above example:

child = document.getElementById('child'); parent = document.getElementById('parent'); child.addEventListener("click", alertName); Onclick parent. AddEventListener ("click", alertName);Copy the code

The first argument to the addEventListener method is the event name and the second argument is the response function. Open the browser and click. Is it the same as the onclick binding? So what’s the benefit of that? Let’s try adding the following code after the previous section:

    function alertAgain(e) {
        alert(this.id + ", again")
    }
    child.addEventListener("click", alertAgain);

Copy the code

Click on the inner blue div section again, and you’ll see that the two click-event methods bound to the element respond in the same order as they were bound in the code. Therefore, using addEventListener does not overwrite previously bound methods.

Capture phase event response

The addEventListener method takes a third parameter. The parameter is called useCapture and is of type Boolean. As you might guess from the name, if it is set to true, event processing will be captured, meaning events will respond in the capture phase, not the bubble phase. Let’s change the addEventListener part of the example above:

child = document.getElementById('child'); parent = document.getElementById('parent'); child.addEventListener("click", alertName); Onclick parent. AddEventListener ("click", alertName, true); parent.addEventListener("click", alertAgain);Copy the code

Test that when the inner blue part is clicked, the alertName method of the outer div responds first, then the alertName method of the inner div, and finally the alertAgain method of the outer div. That is, the statement parent. AddEventListener (“click”, alertName, true); Registered events are responded to in the capture phase, that is, the outermost response is executed first.

Note that IE8 previously did not support addEventListener, so the only way to add an event binding is to use the attachEvent method:

child = document.getElementById('child'); parent = document.getElementById('parent'); child.attachEvent("onclick", alertName); Onclick parent. AttachEvent ("onclick", alertName);Copy the code

The attachEvent method has no third argument, so older versions of IE only support event bubblings, not event capture. Even if the addEventListener method can be called in later versions of Internet Explorer, note that the “this” pointer points differently from the W3C standard browser. When the parent responds during bubbling, the “this pointer” points to the Window object, so when the above example was tested in higher versions of IE, the second “alert” content would be undefined. Oh, bloody IE!

Preventing an event from spreading

Sometimes we want the event to be responded to once and then stop, and we don’t want the child (during capture) or parent (during bubbling) to respond to the same event again. How do we do that? Let’s look at examples. This time we’re going to change the alertName method.

    function alertName(e) {
        alert(this.id);
        stopBubble(e);
    }

    function stopBubble(e) {
        if (e && e.stopPropagation)
            e.stopPropagation();    // W3C
        else
            window.event.cancelBubble = true;  // Old IE
    }

Copy the code

The stopPropagation method on the Event object prevents events from propagating further along the parent node (bubble phase) or child node (capture phase). So after the above changes, we see that the click event is only responded to once. Note that if there are other event response functions on the node currently processing the response, this will still be called. StopPropagation simply prevents the event from propagating to other nodes. Evil in previous versions IE does not support stopPropagation (should be IE8 upwards), so use window. The event. The cancelBubble = true to prevent the spread of events.

To conclude, we introduced event capture and event bubbling in the event processing model. In the W3C standard, an event response enters the capture phase and propagates from the outermost element to the target element that triggers the event. It then enters the bubble phase and propagates outward from the target element, all the way to the outermost element. We can use the third parameter of the addEventListener method to determine whether the event is handled by the capture phase or the bubble phase. The stopPropagation method on the event object prevents the event from propagating again. At this point, what’s the point of an event processing model? Personally, I think there are two advantages:

  1. First, if the parent element has many children bound to the same event, and the event is handled in a similar way, we can simply bind the event to the parent element for uniform processing, so that the code is much cleaner.

  2. When an event occurs, the current element and all elements along its path will handle the event differently, so we can bind each element to its own handling of the event. This means that each element handles its own response function when an event occurs, sort of like a chain of responsibility pattern.

Finally, two caveats:

  1. Blur, Focus, Load, unload events do not propagate.

  2. StopPropagation does not prevent the default behavior of an object from propagating, such as the submission of a button of type “Submit”. If you still want to do this, then prevent the default behavior of events. The method is as follows:

    <a href="http://www.baidu.com/" id="link">Baidu</a>
    <script type="text/javascript">
    function stopDefault(e) {
        if (e && e.preventDefault)
            e.preventDefault();    // W3C
        else
            window.event.returnValue = false;  // Old IE
    }

    link = document.getElementById('link');
    link.onclick = function(e) {
       alert("Prevent open the link of Baidu");
       stopDefault(e);
    }
    </script>

Copy the code

The preventDefault method on the Event object prevents the event from being handled by default, as in the above example, the Baidu page will not open when the link is clicked.