How Passive Event Listeners Improve Scroll Performance

This article explains what passive event listeners are, the scrolling bottleneck they resolve in JavaScript, and how to implement them to enhance web application responsiveness. You will learn how modern browsers process user interactions, why traditional event listeners cause input lag during scrolling, and how enabling passive listeners allows the browser to deliver smooth, jank-free scrolling on touch and wheel events.

The Problem: Scrolling Janky and Thread Blocking

When a user scrolls via a touch screen or a mouse wheel, the browser must decide whether to perform the default action (scrolling the page) or prevent it. The browser does this by executing any JavaScript callbacks bound to events like touchstart, touchmove, wheel, or mousewheel.

In standard event listeners, the callback function has the ability to call event.preventDefault(), which cancels the scroll. Because the browser cannot predict whether the callback will execute preventDefault(), it is forced to wait until the JavaScript code on the main thread finishes running before it can scroll the viewport.

If the main thread is busy with complex computations, DOM updates, or heavy JavaScript execution, this delay leads to noticeable latency, stuttering, and dropped frames—a phenomenon commonly referred to as “scroll jank.”

What Are Passive Event Listeners?

A passive event listener is an event listener configured with a specific flag that informs the browser in advance: this listener will never call preventDefault().

By guaranteeing that the default behavior will not be blocked, passive event listeners allow the browser’s compositor thread to scroll the page immediately upon user input, completely independent of whatever work is happening on the JavaScript main thread.

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

How to Implement Passive Event Listeners

To mark an event listener as passive, pass an options object containing passive: true as the third parameter to addEventListener.

// Standard syntax
element.addEventListener('touchstart', handleTouch, { passive: true });
element.addEventListener('wheel', handleScroll, { passive: true });

Browser Defaults

Modern browsers automatically treat touchstart and touchmove listeners on the window, document, and body as passive by default to ensure baseline scrolling performance. However, custom scroll containers, specific DOM nodes, and wheel listeners elsewhere may still default to non-passive unless explicitly specified.

When to Use Passive Event Listeners

Use passive event listeners whenever you need to monitor user gestures or scrolling without altering the default scrolling behavior. Common use cases include:

When Not to Use Them

Do not set passive: true if your application must intercept and cancel native scrolling or zooming. Examples include: