Introducing Intersection Observer

Have you written JavaScript to enable infinite scrolling or to lazy-load images that appear below the fold?

If you're like me, you did this by listening for scroll events and requesting an element's scrollTop property or calling getBoundingClientRect() on an element. You forced the browser to stop everything and repeatedly recalculate the layout. You fought jank. You suffered. Your users suffered. It was a dark time.

Guess what? The dark times are over! I have learned about the glorious IntersectionObserver and I want to share the good news with you.

The Intersection Observer API is a relatively new web API that provides easy-to-use, efficient tools for detecting changes to a DOM element's visibility within the browser viewport or within another element. No more jank. No more reflow. No more suffering. None.

How it works

When you create an IntersectionObserver, you pass it a callback that is triggered when the visibility of a target element changes.

let observer = new IntersectionObserver(callback, options);
let target = document.querySelector("#target");

An optional options object allows you to fine-tune how the callback gets triggered. You can customize three available options: root, rootMargin, and threshold.

let options = {
  root: document.querySelector(".container"), // takes a DOM element
  rootMargin: "10px 20px", // takes a string indicating margins
  threshold:  0.75 // takes a number or an array of numbers between 0 and 1

root: By default, the callback runs whenever the target element's visibility changes with respect to the browser's viewport. However, if the viewport you care about is within the DOM–if it's, say, a container div that holds a slideshow–you can specify that element here.

rootMargin: The default rootMargin is 0. You can expand or contract the bounding box of the root viewport by specifying margins. For example, if you set rootMargin to 20px, your callback will be triggered when your target comes within 20px of the viewport's edge.

threshold: The default value for the threshold option is 0. This means that as soon as any bit of the target is visible, even just a pixel, the callback is triggered. You might want to wait until 50% of the target is visible before your callback runs. No problem: set the threshold to 0.5. Want to wait until the target is completely visible? Set the threshold to 1. Alternatively, if you would like your callback to fire every time visibility changes by 25%, you could set it to [.25, .5, .75, 1].

When your callback is triggered, according to the options you have set, it receives two parameters: entries and observer. The observer parameter is the IntersectionObserver that launched the callback. Entries contains a list of objects created when a threshold is crossed. Each entry contains interesting information including an isIntersecting boolean that indicates whether your target is visible, again according to your options, and an intersectionRatio value that returns the percent visibility of the target. You can use this handy info to direct the action of your callback.

Browser support

This brief, exciting tour of the Intersection Observer API no doubt has you asking: "Can I use this today?!" Yes! You can! With a couple caveats. IntersectionObserver is supported in recent versions of Chrome, Edge, and Firefox. It's under development for Safari. It's not supported by IE. But never fear: There is a polyfill that will extend support to Safari and IE.


There is more to learn about the Intersection Observer API. I haven't even mentioned unobserve! Here are some references to assist your learning.


A few simple examples on Codepen that illustrate what is covered above: