All topics
DOMintermediate

Event Bubbling and Capturing

The two phases an event travels through the DOM tree — capturing down from the root, then bubbling back up from the target — and how to hook into either.

When a DOM event fires — a click, a keypress, an input change — it doesn't just happen at the exact element it originated on; it travels through the DOM tree in a well-defined path with three phases, and understanding this path is essential for correctly attaching listeners, especially in nested UI structures. This is a heavily tested interview topic because it directly explains both a common source of bugs and a widely-used optimization pattern (event delegation).

Event bubbling is like dropping a pebble into a pond at a specific point (the target) and watching ripples travel outward (bubble up) past every ring (ancestor element) around it — anyone stationed at any ring who's listening for ripples will feel it pass by, in order, from the center outward.

Key Concepts

1
The full event flow has three phases: capturing, where the event starts at the window/document root and travels *down* through each ancestor toward the target element; target, where the event reaches the actual element it originated on; and bubbling, where the event then travels back *up* from the target through the same chain of ancestors toward the root. By default, addEventListener(type, handler) attaches the handler for the bubbling phase; passing true (or { capture: true }) as the third argument attaches it for the capturing phase instead.
capturingtargetbubblingwindowaddEventListener(type, handler)
2
Bubbling is why a click on a deeply nested <span> inside a <button> inside a <div> triggers click handlers on the span, then the button, then the div, in that order, unless something stops it. event.stopPropagation() halts the event from continuing to travel further up (or down, if called during capturing) the chain, preventing ancestor (or descendant) handlers from ever seeing it — a powerful but sometimes overused tool, since stopping propagation can silently break other code elsewhere in the app that legitimately expected to observe that event bubble past.
<span><button><div>event.stopPropagation()
3
Understanding bubbling is the direct prerequisite for event delegation (attaching a single listener to a shared ancestor to handle events from many current or future descendant elements) and for correctly reasoning about nested clickable UI, where accidentally triggering both an inner and outer handler (or needing to prevent that) is an everyday real-world concern.