IntersectionObserver
An API that provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document viewport.
When to Use
- Lazy loading images or heavy components.
- Implementing infinite scrolling interfaces.
- Reporting visibility of advertisements.
- Triggering animations when elements scroll into view.
When NOT to Use
- Tracking precise pixel-by-pixel scrolling (use passive scroll listeners).
- Detecting overlaps of elements that do not involve the viewport/scroll container.
Advantages vs Limitations
Advantages
- Completely asynchronous; it does not run on the main thread during scroll events.
- Eliminates the need for expensive getBoundingClientRect() calls inside scroll event listeners.
- Highly customizable using rootMargin to preload content before it enters the viewport.
Limitations
- Cannot detect if an element is obscured by another visually (e.g. z-index overlap or opacity: 0).
- Callbacks are fired asynchronously, meaning there is a slight delay (not frame-perfect for synchronized parallax).
Production Examples
Lazy Loading Images Efficiently
This creates a single observer that tracks multiple images. As soon as an image is within 100px of the viewport, its actual source is loaded, and it is unobserved to free memory.
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
obs.unobserve(img); // Stop observing once loaded
}
});
}, { rootMargin: '100px' });
document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));Interview Readiness
Q: Why is IntersectionObserver better for performance than listening to the "scroll" event?
A: Listening to the scroll event fires synchronously on the main thread multiple times per frame. To check visibility, developers had to call getBoundingClientRect(), forcing synchronous layout recalculations (thrashing). IntersectionObserver calculates this asynchronously off the main thread.
Related Knowledge
Recommended Recipes
Large Data Table Performance
When rendering a table with thousands of rows (e.g., an analytics dashboard or user directory), the browser must create tens of thousands of DOM nodes. This completely freezes the main thread, delays interactivity, and consumes massive amounts of RAM.
Dashboard Rendering & Layout Thrashing
Dashboards pack high-density information (charts, grids, stats) into a single view. When multiple widgets render, fetch data, and resize simultaneously, they frequently read from and write to the DOM concurrently. This triggers severe layout thrashing and jank.