IntersectionObserver in JavaScript: knowing when an element reaches the screen

IntersectionObserver tells you when an element enters or leaves the screen, with no scroll listener. The browser runs the calculation for you.
3 min read
Believemy logo

Revealing a card as it reaches the screen, loading an image at the last moment, counting the sections actually read: three needs that come up on almost every long page.

The historical method was to listen to scrolling and measure positions on every pixel traveled. Expensive, imprecise, and responsible for plenty of stutter. The browser can run that calculation itself.


Definition

IntersectionObserver watches the overlap between an element and a reference area, the window by default. It calls a callback function when that element enters the area or leaves it, with no scroll listener involved.

JAVASCRIPT
const observer = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      entry.target.classList.add("visible");
      observer.unobserve(entry.target);
    }
  }
}, { threshold: 0.25 });

document.querySelectorAll(".card").forEach((card) => {
  observer.observe(card);
});

A single observer tracks as many elements as you hand it. The call to unobserve drops the card from the watch list once revealed: an entrance animation has no business playing twice.


The three options

The second argument of the constructor tunes the sensitivity. The table lays out its three options across three columns: the option, its default value, and what it controls.

OptionDefaultWhat it controls
rootnullThe reference area. Left empty, it is the browser window
rootMargin"0px"A margin that grows or shrinks that area
threshold0How much of the element must show to trigger the call

A margin of "200px 0px" fires the call two hundred pixels before the element actually arrives, which gives an image time to load. A threshold also accepts an array, [0, 0.5, 1], to be notified at several steps.


What each entry holds

The callback receives an array, never a single element: several elements can cross the boundary within the same frame.

  • isIntersecting: a Boolean telling whether the element overlaps the area.
  • target: the element in question, essential since the array mixes several of them.
  • intersectionRatio: the visible share, from 0 to 1.
  • boundingClientRect: the measured dimensions, already computed by the browser.
Good to know

Thresholds compare visible area, not height. An element taller than the window will never reach a threshold of 1, and the call you expected will simply never come.


Frequently asked questions

Question

Why does the callback fire right after loading?

Because the observer reports the initial state of every element handed to it, including those far off screen. Those first calls carry an isIntersecting set to false, which is why testing that property matters rather than assuming a call means an element appeared.


Question

Is it still the right tool for lazy-loading images?

Not really: the loading="lazy" attribute covers images and embedded frames in one line of HTML, with no script at all. The observer keeps its value for entrance animations, infinite scrolling, and measuring which sections were genuinely seen.


Question

How do I stop watching?

unobserve drops one element, disconnect stops everything at once. The second call is essential when a component leaves the page: without it, the observer keeps a reference to removed elements, which can then never be released from memory.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.