Event Propagation

In the following code a click on the img will generate a click event on the a, li, ul and all the way through the element's ancestors.

<ul>
    <li><a><img></a>
    <li><a><img></a>
    <li><a><img></a>
</ul>
const all = Array.from(document.querySelectorAll('ul, li, a, img'));
all.forEach(b => b.addEventListener('click', (e) => {
    // logs the img, a, li then ul
    console.log(b);
}))

The propagation is bidirectional, from the window to the event target and back. This propagation can be divided into three phases:

  • From the window to the event target parent: this is the capture phase
  • The event target itself: this is the target phase
  • From the event target parent back to the window: the bubble phase

Event Capture Phase

In this phase only the capturer listeners are called, namely, those listeners that were registered using a value of true for the third parameter of addEventListener(). During this phase, the capturers found on the way from the window to the event target parent are called.

const as = Array.from(document.querySelectorAll('a'));
as.forEach(a => a.addEventListener('click', (e) => {
    // From the outermost element inwards
    // the event on the a will run before the event on the img
    console.log(2);
}, true))

Event Target Phase

In this phase all the listeners registered on the event target will be invoked, regardless of the value of their capture parameter.

Event Bubbling Phase

This final stage, this occurs on the way back from the target up to the window. Only the event listeners with a third parameter of false - or underfined which reverts to false - will be called.

Stopping Propagation

The propagation can be stopped using the stopPropagation() method. This means that listeners registered on items on the propagation path on the way back up from the current target will not be called.

const imgs = Array.from(document.querySelectorAll('img'));
imgs.forEach(img => img.addEventListener('click', (e) => {
    // won't go up to the <a> event
    e.stopPropagation();
}))

Stopping Immediate Propagation

This will prevent any further propagation at any stage, event from the window down to the event target

const as = Array.from(document.querySelectorAll('a'));
as.forEach(a => a.addEventListener('click', (e) => {
    // will never reach a click event on the img inside it
    e.stopImmediatePropagation();
}, true))

Refs