In JavaScript event handling DOM Level 2, why does the target get the "bubbling" first, and then "capturing"?

151 Views Asked by At

As I understand it, the DOM Level 2 event handling works in the following order:

  1. capturing from the top HTML element all the way to before target
  2. the target itself
  3. bubbling all the way back the top HTML element

example is at: https://jsfiddle.net/uwe5dmxw/
(I will include the code at the end of this question)

But if I click on the "child" element (the lowest level descendent) on the current Google Chrome, Firefox, Safari, and even on IE 11, I get consistent result in this order:

  1. HTML capturing
  2. BODY capturing
  3. Parent capturing
  4. target bubbling
  5. target capturing
  6. Parent bubbling
  7. BODY bubbling
  8. HTML bubbling

That is, the order of the "target capturing" and "target bubbling" is reversed.

As I understood it, although the DOM Level 2 said event reaches target only once, but most browsers implement it as reaching it twice, once during event capturing and once during event bubbling. But the fact is, why is "target capturing" and "target bubbling" reversed?


Code: (but just a demo, you don't need to look at it if not necessary)

<div id="hi">
  hello
  <div id="child">
    child
  </div>
</div>

JavaScript:

var parentElement = document.getElementById("hi"),
  childElement = document.getElementById("child"),
  htmlElement = document.getElementsByTagName("html")[0],
  bodyElement = document.getElementsByTagName("body")[0];

// ------------------ Bubble --------------------

htmlElement.addEventListener("click", function() {
  console.log("<html> clicked " + new Date().getTime(), this);
});

bodyElement.addEventListener("click", function() {
  console.log("<body> clicked " + new Date().getTime(), this);
});

parentElement.addEventListener("click", function() {
  console.log("Parent clicked " + new Date().getTime(), this);
});

childElement.addEventListener("click", function() {
  console.log("Child clicked at " + new Date().getTime(), this);
});

// ------------------ Use Capture --------------------

htmlElement.addEventListener("click", function() {
  console.log("<html> (useCapture) clicked " + new Date().getTime(), this);
}, true);

bodyElement.addEventListener("click", function() {
  console.log("<body> (useCapture) clicked " + new Date().getTime(), this);
}, true);

parentElement.addEventListener("click", function() {
  console.log("Parent (useCapture) clicked " + new Date().getTime(), this);
}, true);

childElement.addEventListener("click", function() {
  console.log("Child (useCapture) clicked at " + new Date().getTime(), this);
}, true);
1

There are 1 best solutions below

3
On

When an event occurs on an object with multiple event handlers, the event handlers are fired in the order they are attached. You're not seeing a separate event on child. You're seeing the same event (the click event) that is sent to both the event handlers, but sent in the order the event handlers were attached.

If you reverse the order that the event handlers are assigned, you will see the event handlers change the order they are called too. The event is "at target", not bubbling or capturing so both are called.

If you log e.eventPhase for each of the two target event handlers, you will see that it logs the value of 2 which means it is "at the target" (not bubbling or capturing). See the doc on MDN for more info on e.eventPhase.