DOM Event Propagation

DOM event propagation dispatches an event along a path through the target’s ancestors, with capture and bubbling phases where applicable. Understanding the phases and the difference between propagation, the event target, and default actions is essential for reliable event handling and delegation.

When an event occurs on an element, the browser dispatches it through a path involving that element and its ancestors. This lets listeners on different parts of the page respond to the same interaction. The event object exposes `target`, the original event target, and `currentTarget`, the node whose listener is currently executing.

For events that use the standard capture and bubble phases, dispatch begins with capture from the outer path toward the target. At the target, target-phase listeners run; bubbling listeners then run from the target back toward the outer path. A listener can opt into capture with `{ capture: true }`; the default is bubbling-phase registration. Not every event bubbles, so check the event’s behavior when relying on delegation.

`stopPropagation()` prevents the event from continuing to later nodes along the path, but it does not cancel the event’s default browser action. `stopImmediatePropagation()` also prevents other listeners on the same node from running. `preventDefault()` requests cancellation of a cancelable default action; it does not stop propagation. These controls solve different problems.

Propagation enables event delegation: attach a listener to a stable ancestor and inspect the event target to handle interactions from descendant elements. Since the target may be a nested child inside a control, delegation often uses `closest()` and verifies that the result belongs to the ancestor handling the event. Shadow DOM can alter the visible event path; `composedPath()` helps inspect the path exposed to a listener.

Key points

Interview questions

What happens when a DOM event propagates?

Propagation is the dispatch of an event along a path through the DOM. In the capture phase it travels from outer ancestors toward the target; target listeners run at the target, and bubbling listeners then run back outward when the event supports bubbling. This allows a parent to observe interactions originating on a child. `target` stays tied to the origin, while `currentTarget` changes for each listener.

How would you use event delegation for a list with many buttons?

Delegation places a listener on a common ancestor and handles matching descendant interactions there. It reduces the number of listeners and naturally covers descendants added later, provided the event bubbles to that ancestor. Because the event target may be a nested element, use `closest()` to find the intended control and ensure it is within the delegation root. Delegation is unsuitable when the event does not bubble unless an alternate strategy is used.

Compare `stopPropagation()`, `stopImmediatePropagation()`, and `preventDefault()`.

`stopPropagation()` prevents further traversal to other nodes on the event path, but does not cancel the browser’s default action. `stopImmediatePropagation()` has the same path-stopping effect and additionally blocks later listeners on the current target. `preventDefault()` instead cancels a cancelable default action and leaves propagation alone. Choose based on whether the goal is to control listener delivery, the default action, or both.

Explain the event dispatch phases and what propagation controls do not guarantee.

The DOM dispatch path has capture, target, and bubbling stages for events that participate in those phases. Capture listeners are registered with the `capture` option; ordinary listeners are generally registered for bubbling. At the target, capture and non-capture listeners can both be relevant, with ordering governed by the DOM event dispatch rules. Code should not assume every event bubbles or that stopping propagation cancels its default action.