Passive Event Listeners
Passive event listeners tell the browser that a listener will not cancel the event with `preventDefault()`. For applicable input events, this can let the browser proceed with scrolling without waiting for JavaScript, improving responsiveness.
A passive listener is created by passing `{ passive: true }` as the options argument to `addEventListener()`. The important semantic guarantee is that the listener will not call `preventDefault()` to cancel the event.
This matters most for input events that can participate in scrolling, such as `touchstart`, `touchmove`, and `wheel`. When the browser knows a listener is passive, it can avoid waiting for that listener to determine whether the default scrolling behavior should be canceled.
If a passive listener calls `event.preventDefault()`, the browser does not allow that cancellation. In browser implementations, this typically produces a console warning rather than preventing the default action, so code that genuinely needs to cancel scrolling should not use a passive listener for that purpose.
The option is independent of whether the listener uses capture. For example, `{ passive: true, capture: true }` requests both passive behavior and capture-phase dispatch. When removing a listener, `removeEventListener()` primarily needs the same listener function and matching capture setting; passive is not the identity-defining option.
Key points
- `{ passive: true }` promises that the listener will not cancel the event with `preventDefault()`.
- Passive listeners can improve responsiveness for scroll-related input events because the browser does not need to wait for cancellation decisions.
- `preventDefault()` is ineffective for canceling an event from a passive listener.
- Passive and capture are separate event-listener options.
- Use a non-passive listener when canceling the event's default behavior is required.
Interview questions
What is a passive event listener, and why would you use one?
A passive listener is registered with `{ passive: true }` and promises not to call `preventDefault()`. This gives the browser more freedom to perform default actions such as scrolling without waiting for JavaScript to decide whether they should be canceled. It is particularly useful for touch and wheel input where scroll responsiveness matters.
What happens if you call `preventDefault()` inside a passive listener?
The listener cannot use `preventDefault()` to cancel the event when it is passive. The browser generally ignores the cancellation request and may report a console warning. If cancellation is required, the listener should be registered without passive behavior.
How can passive listeners improve scroll performance?
For certain input events, the browser may need to know whether JavaScript intends to cancel the default scrolling action. A non-passive listener can therefore create a point where the browser has to account for JavaScript before committing to scrolling. A passive listener removes that cancellation possibility, allowing the browser to proceed without waiting for that decision.
Can a listener be both passive and capturing?
Yes. The options are independent, so a listener can use `{ passive: true, capture: true }`. `capture` determines which phase of event propagation the listener participates in, while `passive` determines whether the listener may cancel the event's default action. Combining them does not make passive behavior equivalent to capture behavior.
You need to disable native scrolling during a touch interaction. Should the listener be passive?
No, not if the implementation relies on `preventDefault()` to cancel the browser's scrolling behavior. A passive listener explicitly gives up that cancellation ability. I would register the relevant listener as non-passive and use `preventDefault()` only where necessary, while considering the performance implications and limiting the scope of the non-passive handling.