Listener Options

Listener options control important aspects of how a browser event listener behaves. The most commonly discussed options are `capture`, `once`, `passive`, and `signal`.

The `addEventListener()` method accepts an options object that can change when and how a listener runs. Instead of simply passing a callback, you can provide settings such as `{ capture: true }` or `{ once: true }`.

`capture` determines which phase of event propagation invokes the listener. With `capture: true`, the listener participates during the capturing phase as the event travels toward its target; with the default `false`, it normally runs during the bubbling phase.

`once: true` causes the browser to remove the listener automatically after its first invocation. This is useful for one-time interactions such as initialization triggered by the first user action.

`passive: true` tells the browser that the listener will not call `preventDefault()`. This can allow the browser to optimize certain input and scrolling behavior. Calling `preventDefault()` from a passive listener is not permitted to cancel the event.

`signal` associates the listener with an `AbortSignal`. Aborting its corresponding `AbortController` removes the listener, which is useful when a component or other owner needs to clean up several event subscriptions together.

Key points

Interview questions

What does the `capture` option do?

`capture` determines which propagation phase the listener participates in. When it is `true`, the listener runs during event capturing as the event travels from an ancestor toward its target. When it is `false`, which is the usual default, the listener participates on the target/bubbling side of propagation.

Why would you use `once: true`?

`once: true` is useful when a listener should respond only to the first matching event. The browser automatically removes the listener after it invokes the callback. This avoids writing separate cleanup logic for simple one-time interactions.

What does `passive: true` mean, and why can it matter for scrolling?

`passive: true` tells the browser that the listener will not call `preventDefault()` to cancel the event. This can help the browser handle certain input and scrolling interactions without having to wait for the listener to determine whether the default action should be canceled. If code attempts to use `preventDefault()` from a passive listener, the event cannot be canceled that way.

How can `AbortController` be used with event listeners?

Create an `AbortController` and pass its `signal` to the listener through the `signal` option. Calling `controller.abort()` then removes listeners associated with that signal. This provides a convenient lifecycle-based cleanup mechanism, especially when several resources share the same controller.

How would you combine listener options in a component with a finite lifecycle?

I would create an `AbortController` for the component lifecycle and pass its signal to listeners that should disappear when the component is destroyed. I would use `once` for genuinely one-shot events and `capture` only when the propagation phase requires it. I would use `passive` when the handler does not need to cancel the event, particularly for relevant input or scrolling handlers. On teardown, aborting the controller provides centralized listener cleanup.