Event Bubbling and Capturing in JavaScript

This article provides a clear overview of the JavaScript event propagation model, focusing specifically on event capturing and event bubbling. You will learn the three phases of the DOM event flow, how events travel between nested HTML elements, how to configure event listeners for capturing or bubbling, and how to stop propagation when necessary.

The Three Phases of Event Flow

When an event occurs in a web browser—such as a click on a button inside nested div containers—the browser executes the event in three distinct phases defined by the standard DOM Level 2 Event Model:

  1. Capturing Phase (Trickling): The event starts at the window object and travels downward through the DOM tree until it reaches the target element.
  2. Target Phase: The event reaches the actual element that initiated the interaction (the event.target).
  3. Bubbling Phase: The event travels back up the DOM tree from the target element to the window object.
Window -> Document -> <html> -> <body> -> Parent -> [ Target Element ]  (Capturing Phase)
                                                           |
                                                      Target Phase
                                                           |
Window <- Document <- <html> <- <body> <- Parent <- [ Target Element ]  (Bubbling Phase)

What is Event Bubbling?

Event bubbling is the default behavior in modern JavaScript. When an event fires on an inner child element, it automatically triggers the same event on its parent elements, continuing upward all the way to document and window.

Example of Event Bubbling

Consider the following HTML structure:

<div id="parent">
  <button id="child">Click Me</button>
</div>
document.getElementById('parent').addEventListener('click', () => {
  console.log('Parent clicked');
});

document.getElementById('child').addEventListener('click', () => {
  console.log('Child clicked');
});

When you click the button (#child), the console outputs: 1. Child clicked 2. Parent clicked

Because bubbling moves bottom-up, the child listener fires first, followed by the parent listener.

What is Event Capturing?

Event capturing (also referred to as trickling) is the reverse of bubbling. In this phase, the event starts at the topmost ancestor and moves downward toward the target element.

By default, event listeners do not respond to the capturing phase. To execute a handler during capturing, pass true or { capture: true } as the third parameter to addEventListener.

Example of Event Capturing

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

document.getElementById('child').addEventListener('click', () => {
  console.log('Child clicked');
});

When you click the button, the console outputs: 1. Parent clicked (Capture) 2. Child clicked

The parent intercepts the event before it reaches the child.

Stopping Event Propagation

You can prevent an event from continuing through the capturing or bubbling phases by calling event.stopPropagation() inside your handler.

document.getElementById('child').addEventListener('click', (event) => {
  event.stopPropagation();
  console.log('Child clicked, propagation stopped');
});

If multiple listeners are attached to the exact same element for the same event type, event.stopImmediatePropagation() will stop propagation to parent elements as well as prevent any remaining listeners on the current element from executing.

Why Bubbling Matters: Event Delegation

Understanding bubbling enables event delegation, a pattern where you place a single event listener on a parent element instead of attaching individual listeners to multiple child elements.

document.getElementById('parent-list').addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    console.log('List item clicked:', event.target.textContent);
  }
});

Because child click events bubble up to #parent-list, the single listener handles all present and dynamically added <li> elements, improving memory efficiency and performance.