Event Capturing in JavaScript addEventListener

Event capturing is a phase in the JavaScript event propagation cycle where an event trickles down through the DOM hierarchy from the root ancestor to the target element. While standard event handling defaults to the bubbling phase (moving from the target element upward), event capturing allows developers to intercept and execute event listeners before they reach the inner target. This article explains the fundamentals of event capturing, how it fits into the event propagation lifecycle, and how to enable it using the addEventListener method.

Understanding the Event Propagation Cycle

When an event occurs in the DOM (such as a click or keypress), it passes through three distinct phases:

  1. Capturing Phase: The event starts at the window and travels down through the DOM tree (Document -> <html> -> <body> -> parent elements) until it reaches the target element.
  2. Target Phase: The event arrives at the target element where the user initiated the action.
  3. Bubbling Phase: The event travels back up the DOM tree from the target element to the window.

By default, standard event listeners attached using JavaScript listen only during the bubbling phase.

Enabling Event Capturing in addEventListener

To listen for an event during the capturing phase rather than the bubbling phase, you must use the optional third argument of the addEventListener method.

The addEventListener method accepts three arguments: - type: The event name (e.g., 'click'). - listener: The callback function executed when the event triggers. - options / useCapture: A boolean or an options object configuring the behavior of the listener.

Method 1: Using a Boolean Parameter

Pass true as the third argument to enable capturing:

const parentElement = document.querySelector('#parent');

parentElement.addEventListener('click', (event) => {
    console.log('Parent clicked (Capturing Phase)');
}, true);

If set to false (the default value), the listener executes during the bubbling phase.

Method 2: Using the Options Object

Pass an options object containing the property { capture: true }:

const parentElement = document.querySelector('#parent');

parentElement.addEventListener('click', (event) => {
    console.log('Parent clicked (Capturing Phase)');
}, { capture: true });

This approach allows you to configure additional options simultaneously, such as { capture: true, once: true }.

Execution Order Example

Consider the following nested structure:

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

parent.addEventListener('click', () => {
    console.log('1. Parent (Capture)');
}, true);

child.addEventListener('click', () => {
    console.log('2. Child (Target)');
});

parent.addEventListener('click', () => {
    console.log('3. Parent (Bubble)');
}, false);

When the button is clicked, the console logs: 1. 1. Parent (Capture) 2. 2. Child (Target) 3. 3. Parent (Bubble)

Practical Use Cases for Event Capturing