How Does Event Propagation Work in JavaScript?

In JavaScript, the event propagation process is divided into three distinct phases:

  1. Capturing phase: In this phase, the event is captured by the outermost element and propagated to the innermost element. The capturing phase starts from the top of the DOM hierarchy and works its way down to the target element. During this phase, the event is propagated through all the parent elements of the target element.
  2. Target phase: Once the event reaches the target element, it enters the target phase. In this phase, the event is handled by the target element. If there are any event listeners attached to the target element, they will be executed.
  3. Bubbling phase: After the event is handled by the target element, it enters the bubbling phase. In this phase, the event bubbles up from the target element to the outermost element in the DOM hierarchy. During this phase, the event is propagated through all the parent elements of the target element. If there are any event listeners attached to any of the parent elements, they will be executed. The bubbling phase ends when the event reaches the outermost element.

Having three phases of event propagation means that you can control the order in which event handlers are executed on different elements in the DOM hierarchy. By default, events propagate through all the parent elements in the capturing phase, reach the target element in the target phase, and then propagate through all the parent elements again in the bubbling phase.

This means that if you have two event listeners attached to the same element, one in the capturing phase and one in the bubbling phase, then both event listeners will execute when the event is triggered.

The order in which the event listeners are executed depends on their respective phases. In general, the capturing event listener will be executed first, followed by the target event listener, and then the bubbling event listener.

You can use the Event.eventPhase property to check the current phase of the event propagation. The value of the eventPhase can be one of the following:

  1. Event.CAPTURING_PHASE (or 1): indicates that the event is in the capturing phase;
  2. Event.AT_TARGET (or 2): indicates that the event is in the target phase;
  3. Event.BUBBLING_PHASE (or 3): indicates that the event is in the bubbling phase.

You can specify whether an event should be handled in the capturing or bubbling phase by passing true or false respectively as the third argument to the EventTarget.addEventListener() method.

For example:

<div id="parent">
  <button id="child">Click me</button>
</div>
const parent = document.getElementById('parent');

parent.addEventListener('click', function(event) {
  console.log('Parent Capturing Phase', event.eventPhase);
}, true);

parent.addEventListener('click', function(event) {
  console.log('Parent Bubbling Phase', event.eventPhase);
}, false);
const child = document.getElementById('child');

child.addEventListener('click', function(event) {
  console.log('Target Phase', event.eventPhase);
}, false);

If you click the button in this example, the following output will be logged in the console:

"Parent Capturing Phase", 1
"Target Phase", 2
"Parent Bubbling Phase", 3

As you can see in this example, the parent event handler is run for both, capturing and bubbling phase.

Since the child is the target in this example, it will always be in the target phase regardless of whether the event is handled in the capturing or bubbling phase (using the EventTarget.addEventListener() method).


This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.