Tutorial · html-css-js · Published 2026-08-16 · 3 min read
IntersectionObserver API for scroll-triggered effects
Use the IntersectionObserver API to detect elements entering or leaving the viewport for lazy loading, animations and analytics.
What it does
The IntersectionObserver API lets JavaScript ask the browser to notify it when a watched element's visibility relative to the viewport (or another container) changes. Instead of attaching a scroll listener and computing positions yourself on every scroll event, you register an observer and the browser reports intersection changes efficiently at the point they happen.
It is Baseline Widely Available, so it works across all current engines with no polyfill. The core pattern is declarative: create an observer with a callback and options, then call observe() on each target, and the callback fires initially (to report the starting state) and whenever the element crosses the configured threshold. Observers run asynchronously, so you do not read scroll positions manually; this is what makes them both simpler and cheaper than a naive scroll handler.
Watch an element
A minimal observer reports when an element enters and leaves the viewport:
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Element is in view", entry.target);
}
});
}, { threshold: 0.2 });
observer.observe(document.querySelector(".reveal"));
The root option changes the container to observe against (the viewport is the default), threshold is the fraction of the target that must intersect before the callback fires (single number or array), and rootMargin expands or shrinks the observed box so you can trigger slightly before the element is actually visible.
For lazy content, entry.isIntersecting tells you when to start loading; for animations, the same signal marks when to add a visible class. In all cases, unobserve the target once the condition is met so the observer stops tracking it, which avoids work that is no longer needed.
Real-world uses
- Lazy loading below-the-fold content. Load an image or a heavy component only when it comes into view, rather than on page load. The lazy loading images guide pairs the native
loading="lazy"attribute with a JavaScript observer where behaviour, not just an image, must react to visibility. - Scroll-triggered reveal animations. Add an entrance class when the element enters view, supporting the animation only after the element is actually visible. This is the accessibility-friendly approach because content stays readable without JavaScript too.
- Sticky and infinite lists. Detect when a sentinel element at the bottom of a container becomes visible to load the next page of results, extending a pattern you could otherwise only approximate with scroll math.
- Measuring visibility for analytics. Report when a section is actually seen, fused with the field-data collection the web-vitals library uses to gather real-user metrics.
Where it crosses into performance, the observer is often the trigger that replaces a scroll listener. If scroll handlers are doing the heavy lifting, moving to an observer keeps the event work off the main thread by eliminating per-frame position recalculations. Combine it with built-in lazy loading for simple images (which cover the common case with an attribute) and reserve an observer for the cases that need actual JavaScript, such as deferred third-party widgets or analytics visibility, so the intersection behaviour stays simple and the page stays responsive.