10/10/2014

[Javascript] Event Bubbling and Capturing Propagating Order

Problem

We have three elements in the DOM shown below. When users click on the element3, all of elements' click handler will be executed. But what's the order of executing handler?
<div class="element1">element1
    <div class="element2">element2
        <div class="element3">element3</div>
    </div>     
</div>

The W3C Event Bubbling and Capturing Propagating Order

According to W3C standard, there are two major phases for propagating event object.
Capturing phase:
Firstly, The event object will propagate through the target's ancestors from the Window to the target's parent (Note that target in this case is element3.)
Bubbling phase:
Then event object propagates through the target's ancestors in reverse order, starting with the target's parent and ending with the Window.
Event Bubbling and Capturing Propagating Order
Event Bubbling and Capturing Propagating Order
So let's add some event listeners to print out the propagating order of event object. Before that, let's see the arguements of addEventListener.
/**
  * @param {String} type The event type
  * @param {Function} listener The event handler
  * @param {Boolean} useCapture If True, use capturing. Otherwise, use bubbling.
  */
  element.addEventListener(type, listener[, useCapture]);
The following code make all elements register a click event for both of bubbling and capturing. Note that registering an event for the same listener doesn't affect the order of event object propagating.
var element1 = document.getElementsByClassName('element1')[0],
    element2 = document.getElementsByClassName('element2')[0],
    element3 = document.getElementsByClassName('element3')[0],
    foo = function(e) { console.log(this.className); };

element1.addEventListener('click', foo); // bubbling
element1.addEventListener('click', foo, true); // capturing
element2.addEventListener('click', foo);
element2.addEventListener('click', foo, true);
element3.addEventListener('click', foo);
element3.addEventListener('click', foo, true);
When you click element3, it will show
element1
element2
element3
element3
element2
element1 
As we expected, the event object will first propagate in capturing phase (top-down) and  then bubbling phase (bottom-up).
By the way, you can stop the propagation by calling e.stopPropagation().
var stop = function(e) { 
    console.log('stop', this.className); 
    e.stopPropagation();
};

element1.addEventListener('click', foo);
element1.addEventListener('click', foo, true); 
element2.addEventListener('click', stop); // Change handler to "stop"
element2.addEventListener('click', foo, true);
element3.addEventListener('click', foo);
element3.addEventListener('click', foo, true);
The output
element1
element2
element3 
element3
stop element2 
The Playground in JS fiddle.