4

If you run this code and click on P, the whole capturing stage is executed but the bubbling stage is stopped as expected on div. What's the issue with this code?

  for(let elem of document.querySelectorAll('*')) {
    elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true);
    elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`));
  }
<form>FORM
  <div onclick="event.stopPropagation()">DIV
    <p>P</p>
  </div>
</form>
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
d9ngle
  • 1,303
  • 3
  • 13
  • 30
  • Did you try `stopImmediatePropagation`? – Bergi Jan 22 '19 at 16:31
  • Just tried, same problem @Bergi – d9ngle Jan 22 '19 at 16:32
  • I believe `event.stopPropagation()` is effective to bubbling phase not capture phase. – zer00ne Jan 22 '19 at 17:23
  • @zer00ne `stopPropagation()` may be used to stop event propagation for events registered in the capturing phase or the bubbling phase. However, during the capturing phase, the first handler processed is the one attached to the outermost parent element (often ``) so it produces a different outcome than stopping event propagation for events registered in the default bubbling phase. – benvc Jan 22 '19 at 19:49

1 Answers1

4

The capturing phase does not get interrupted because in modern browsers events are registered by default in the bubbling phase (so the onclick() event in your <div> element does not interact with the separate event handler you registered in the capturing phase).

I am not aware of a way to register a capturing phase event handler via an html attribute (other than in certain older browsers where events were registered by default in the capturing phase).

See the snippet below for a modified version of your code that I think is probably what you originally intended. You will see that this code calls stopPropagation() inside both the capturing phase and bubbling phase handlers (and results in the expected outcome in both cases).

for (const elem of document.querySelectorAll('*')) {
  elem.addEventListener('click', (event) => {
    if (elem.tagName === 'DIV') {
      event.stopPropagation();
    }
    
    console.log(`Capturing: ${elem.tagName}`);
  }, true);
  
  elem.addEventListener('click', (event) => {
    if (elem.tagName === 'DIV') {
      event.stopPropagation();
    }
    
    console.log(`Bubbling: ${elem.tagName}`);
  });
}
<form>
  form
  <div>
    div (click here)
    <p>p</p>
  </div>
</form>

In the capturing phase, the browser processes the handlers starting with the outermost parent, so it does not encounter stopPropagation() until it has already processed the click handlers for all the parent elements <html>, <body>, etc.

In the bubbling phase, it processes the event handlers starting with the clicked element moving outward through the parent elements.

benvc
  • 14,448
  • 4
  • 33
  • 54