Frontend Development 10 min read

Understanding Event Bubbling and Capturing in JavaScript

This article explains JavaScript’s event flow, detailing the three phases of capturing, target, and bubbling, demonstrates how to use addEventListener with the useCapture flag, and shows practical techniques for handling, delegating, and stopping event propagation in web applications.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Understanding Event Bubbling and Capturing in JavaScript

JavaScript event bubbling is used to capture and handle events that propagate inside the DOM, but many developers are unclear about the difference between bubbling and capturing.

This article discusses everything you need to know about the topic with relevant examples.

Event Flow Propagation

Before introducing event capturing and bubbling, it is useful to understand how an event propagates inside the DOM. When several nested elements handle the same event, the order in which handlers fire can be confusing, so understanding the propagation sequence is essential.

Typically, an event starts at the parent element, travels toward the target element, and then propagates back to the parent.

JavaScript events have three phases:

Capture phase: The event travels from the Window object down toward the target element.

Target phase: The event reaches the target element.

Bubbling phase: The event travels back up from the target toward the Window object.

The diagram below illustrates the lifecycle of event propagation.

What Is Event Capturing?

Event capturing is the initial stage of event propagation, starting from the outermost element and moving toward the element that initiates the event lifecycle.

If an event is bound to the Window object, it will be the first to execute. In the example below, the order of handling is Window , Document , DIV 2 , DIV 1 , and finally button .

Event capturing occurs only on the element that is clicked; the event does not propagate to child elements. You can register a capturing‑phase listener by passing true as the third argument to addEventListener() (the useCapture parameter).

target.addEventListener(type, listener, useCapture)

Below is a test script that demonstrates capturing:

window.addEventListener("click", () => {
    console.log('Window');
}, true);

document.addEventListener("click", () => {
    console.log('Document');
}, true);

document.querySelector('.div2').addEventListener("click", () => {
    console.log('DIV 2');
}, true);

document.querySelector('.div1').addEventListener("click", () => {
    console.log('DIV 1');
}, true);

document.querySelector('button').addEventListener("click", () => {
    console.log('CLICK ME!');
}, true);

What Is Event Bubbling?

If you understand capturing, bubbling is easy to grasp—it is the exact opposite.

Event bubbling starts from a child element, propagates up the DOM tree, and continues until the topmost parent element handles the event.

When the useCapture argument is omitted or set to false , addEventListener() registers a bubbling‑phase listener, which is the default behavior.

The article also shows how to handle events in both phases, using different listeners for Document , DIV 2 , etc., and demonstrates the order in which they fire during capture and bubble.

window.addEventListener("click", () => {
    console.log('Window');
}, true);

document.addEventListener("click", () => {
    console.log('Document');
});

document.querySelector('.div2').addEventListener("click", () => {
    console.log('DIV 2');
});

document.querySelector('.div1').addEventListener("click", () => {
    console.log('DIV 1');
}, true);

document.querySelector('button').addEventListener("click", () => {
    console.log('CLICK ME!');
}, true);

Applications of Capturing and Bubbling

Usually a single global listener is enough to handle events across the document. For example, you can register a listener on the DOM that runs whenever any event occurs.

Similarly, capturing and bubbling can be used to modify the user interface.

When dealing with a table where each cell can be selected, assigning a handler to every cell would cause code duplication. Instead, a single listener on the table can use event bubbling to detect which cell was clicked.

document.querySelector("table").addEventListener("click", (event) => {
    if (event.target.nodeName == 'TD')
        event.target.style.background = "rgb(230, 226, 40)";
});

The listener checks nodeName to match the clicked cell and changes its background color.

How to Prevent Event Propagation

Sometimes uncontrolled bubbling or capturing can be annoying.

Deeply nested structures can also cause performance issues because each event creates a new event cycle.

In such cases, you can call stopPropagation() to prevent the event from traveling further up or down the DOM tree.

document.querySelector('.card').addEventListener("click", () => {
    $("#detailsModal").modal();
});

document.querySelector('button').addEventListener("click", (event) => {
    event.stopPropagation();
    $("#deleteModal").modal();
});

Conclusion

JavaScript event capturing and bubbling can be used to efficiently handle events in web applications. Understanding the event flow and how capturing and bubbling work helps you optimize your app by applying the right event‑handling strategies.

For example, knowing these mechanisms can save you time when troubleshooting unexpected event triggers.

Try the examples above and share your experience in the comments.

Thank you for reading!

JavaScriptDOMaddEventListenerEvent BubblingEvent Capturing
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.