Passive vs Non-Passive Event Listeners in JavaScript

Passive and non-passive event listeners determine how a browser handles user input events—such as scrolling, touch, and wheel interactions—relative to page rendering. While non-passive event listeners can block the main thread and delay scrolling while the browser waits to check if the action will be canceled, passive event listeners inform the browser upfront that the event will not be canceled, allowing the browser to perform smooth scrolling immediately on a separate background thread.

The Mechanism Behind Non-Passive Event Listeners

By default, standard event listeners are non-passive ({ passive: false }). When a user scrolls via a touch gesture (touchstart, touchmove) or a mouse wheel (wheel), the browser generates an event. Because JavaScript allows developers to cancel the default scrolling behavior using event.preventDefault(), the browser must pause scrolling and wait for the JavaScript event listener to finish executing on the main thread.

If the main thread is occupied with heavy computations, layout recalculations, or complex DOM updates, this waiting period causes a noticeable delay between the user’s gesture and the actual movement of the screen. This delay is known as “scroll jank.”

How Passive Event Listeners Improve Performance

Passive event listeners ({ passive: true }) solve scroll jank by indicating to the browser that the handler will never invoke event.preventDefault().

Because the browser is guaranteed that the scroll action cannot be canceled, it does not need to wait for the JavaScript thread to finish executing the callback function. Instead, the browser’s compositor thread handles the scroll animation immediately and asynchronously. The JavaScript event listener still executes, but it runs concurrently without blocking the user’s visual feedback.

If event.preventDefault() is accidentally called inside a passive listener, the browser simply ignores the call and generates a warning in the developer console.

Implementation Comparison

You can specify whether an event listener is passive by passing an options object as the third argument to addEventListener:

// Non-passive listener: May cause scroll lag
window.addEventListener('touchmove', handleScroll, { passive: false });

// Passive listener: Enables smooth, non-blocking scrolling
window.addEventListener('touchmove', handleScroll, { passive: true });

Key Differences