Why input handlers slow down your page
Input handlers are a common source of performance bottlenecks. When a user interacts with your page, they can block frames from completing and trigger unnecessary layout work. The key is understanding when and how these handlers interfere with the browser's rendering pipeline.
In the best case, a user's touch or scroll input is handled entirely by the compositor thread, which moves content around without touching the main thread—the place where JavaScript, style, layout, and paint happen.
However, attaching handlers for events like touchstart, touchmove, or touchend forces the compositor to pause. It must wait for your handler to finish because you might call preventDefault() to cancel the scroll. Even if you never call it, the compositor still waits, which blocks scrolling and can produce stuttering or dropped frames.
The takeaway is straightforward: any input handler you attach should return control to the compositor quickly.
Don't touch styles inside input handlers
Handlers for scroll and touch events run just before the next requestAnimationFrame callback. If you change a visual property inside one of these handlers, those style changes are still pending when the requestAnimationFrame callback starts. If you then read a visual property at the top of that callback—something the guidance on avoiding layout thrashing warns against—you'll force a synchronous layout.
Debounce visual work with requestAnimationFrame
Both problems share the same fix: defer all visual changes to the next requestAnimationFrame callback instead of applying them inside the input handler.
function onScroll (evt) {
// Store the scroll value for laterz.
lastScrollY = window.scrollY;
// Prevent multiple rAF callbacks.
if (scheduledAnimationFrame)
return;
scheduledAnimationFrame = true;
requestAnimationFrame(readAndUpdatePage);
}
window.addEventListener('scroll', onScroll);
This approach keeps input handlers lightweight, so they don't block scrolling or touch response with expensive work, and it avoids the forced synchronous layout that comes from reading styles after making changes.



