How to Fix Scroll Event Performance in JavaScript
In this tutorial, you'll learn about How to Fix Scroll Event Performance in JavaScript. We cover key concepts, practical examples, and best practices.
The Problem
Scroll event listeners cause jank and performance issues because the scroll event fires at a high rate (60+ times per second), triggering expensive layout calculations, repaints, or JavaScript execution on every frame.
Quick Fix
Step 1: Add a passive listener
A passive listener tells the browser it will not call preventDefault(), enabling scroll optimization:
window.addEventListener('scroll', () => {
updateLayout();
});
This blocks scrolling while updateLayout runs. Make it passive:
window.addEventListener('scroll', () => {
updateLayout();
}, { passive: true });
Step 2: Throttle the handler
Limit how often the handler runs using requestAnimationFrame:
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
updateLayout();
ticking = false;
});
ticking = true;
}
}, { passive: true });
Step 3: Use IntersectionObserver instead of scroll
For detecting element visibility, IntersectionObserver is more performant:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.lazy-section').forEach(el => observer.observe(el));
Step 4: Debounce for non-visual updates
For analytics or saving scroll position, use debounce:
function debounce(fn, delay = 150) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
window.addEventListener('scroll', debounce(() => {
saveScrollPosition(window.scrollY);
}, 200), { passive: true });
Prevention
- Always use
{ passive: true }for scroll and touch event listeners. - Use
requestAnimationFrameto throttle scroll handlers. - Replace scroll-based visibility checks with
IntersectionObserver. - Use CSS
overflow-anchor: autofor scroll anchoring instead of JS. - Profile scroll performance in DevTools Performance panel.
Common Mistakes with scroll event
- Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
These mistakes appear frequently in real-world JS code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro