Compile Ready
Module 12 · Browser APIs

Event Capturing

Intermediate8m read7m practice15m total
EventsCapturingPropagationaddEventListener

Introduction

Event capturing is the phase before the target handles the event. The event travels from the top of the tree down toward the target, giving ancestors an early chance to observe or intercept it.

In modern JavaScript, you opt into this phase with addEventListener(type, handler, { capture: true }) or the legacy third argument true.

Why This Matters

Capturing is less common than bubbling, so it is a strong interview differentiator. It matters for global shortcuts, analytics, overlays, and framework internals that need to observe events before local components stop propagation.

Theory

The three phases

The browser dispatch algorithm has three conceptual phases:

  1. Capturing phase: ancestors from the root down to the target's parent.
  2. Target phase: listeners on the target itself.
  3. Bubbling phase: ancestors from the target's parent back upward.

A listener registered with capture: true runs during capture. A listener registered without it runs during target or bubble depending on where it is placed.

The old useCapture flag

The third parameter to addEventListener used to be a boolean: true for capture and false for bubble. Modern code prefers an options object because { capture: true, once: true } is self-documenting and extensible.

Why use capture?

Use capture when an ancestor must run before children: outside-click boundaries, global keyboard policies, analytics that should run before bubbling is stopped, or a modal layer that needs first chance at pointer events.

Caveats

Capturing can surprise teammates because most UI code assumes bubbling. Use it intentionally, name handlers clearly, and avoid changing behavior globally unless the feature truly needs precedence.

Visual Diagrams

Full propagation flow
root capture listener
  |
  v
panel capture listener
  |
  v
button target listeners
  |
  ^
panel bubble listener
  |
  ^
root bubble listener

Capture runs top-down before target; bubbling runs bottom-up after target.

Boolean flag vs options object
addEventListener('click', handler, true)
  means capture

addEventListener('click', handler, { capture: true })
  means capture and can add once, passive, or signal later

The options object is clearer in production code than a mysterious boolean.

Code Examples

Run an ancestor listener during capture

The page-level capture listener runs before the button's regular click listener.

Loading…

Capture with lifecycle cleanup

Use an AbortController when capture listeners belong to a component lifecycle.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1const path = ['root', 'panel', 'button'];
2
3for (let i = 0; i < path.length - 1; i++) {
4 console.log('capture ' + path[i]);
5}
6
7console.log('target ' + path[path.length - 1]);
8
9for (let i = path.length - 2; i >= 0; i--) {
10 console.log('bubble ' + path[i]);
11}

Coding Exercises

Compute the full propagation order

Medium

Implement getPropagationOrder(path) where path is ordered from root to target. Return labels for capture, target, and bubble phases.

Interview Questions

1What is event capturing and how do you register a capture listener?

Capturing is the top-down propagation phase before the event reaches its target. Register a listener with addEventListener('click', handler, { capture: true }) or the older third argument true.

Asked at:GoogleAppleMicrosoft

Follow-ups

  • When would capture be useful?
  • How is it different from bubbling?
2What is the order of event phases?

The order is capturing, target, then bubbling. Capture listeners on ancestors run from outermost to innermost, target listeners run on the target, and bubble listeners on ancestors run from innermost to outermost.

Asked at:MetaAmazon

Quiz

1. How do you opt into the capture phase with modern `addEventListener` syntax?

2. Which phase runs first for an event dispatched to a nested target?

Summary

  • Capturing is the top-down phase before the event reaches the target.
  • Use `{ capture: true }` rather than an unexplained boolean when possible.
  • The full order is capture → target → bubble.
  • Capture is powerful but should be used intentionally because most UI code expects bubbling.

Cheat Sheet

Order: capture → target → bubble.

Register: addEventListener(type, handler, { capture: true }).

Legacy: third argument true means capture.

Use cases: global shortcuts, outside-click boundaries, analytics before child stops propagation.

Caution: surprising if overused; document why a capture listener exists.