Intersection Observer

An incredibly useful JavaScript Web API

Mar 14, 2021

The Intersection Observer API is an incredibly useful Web API. It's spec is currently in working draft, however over the past year (since spring 2020) support for the Intersection Observer API has been solidified across major mobile browsers, extending its broad desktop support. (With the exception of IE, which is EOL anyways. Those of you who lived through "IE{n} support", rejoice!)

What does it do?

From MDN:

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.

In plain English, it tells you when an element comes into view (among other fancy things it can do). If you've ever attached an event handler to window.onscroll to detect when to lazy load an image, or kickoff an animation at the right time, or implement an infinite scroll feature, you've probably experienced the performance impact of same. Optimization methods include requestAnimationFrame, debouncing and throttling.

Now with solid browser support, the Intersection Observer API lets you do away with all of that.

Let's dive in

The best thing about Intersection Observer is that it's so easy to use.

  1. Instantiate an IntersectionObserver
  2. Observe an html element
  3. Invoke a callback function

Here's an example:

const getObserver = (threshold = 1) => {
    return new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                entry.target.classList.add('in-viewport');
            } else {
                entry.target.classList.remove('in-viewport');
            }
        });
    }, {threshold: threshold});
}

This helper function only exposes the threshold option for runtime configuration but the API supports a few other options. In this case a new instance of IntersectionObserver is returned, which allows you to observe one or more html elements. Those are then passed to the defined callback function. Here we are simply adding and removing some CSS classnames.

Given the element <div id="my-observed-element"></div> we can do:

const myObserver = getObserver();
myObserver.observe(document.getElementById('my-observed-element'));

That's it! Now when every pixel of div#my-observed-element is in the viewport, the div will have the classname in-viewport added, and when it's out of viewport the classname will be removed. If we wire up a CSS transition for opacity, it would begin when the element is in view, rather than on page load, like this:

My Observed Element

A quick googling for "intersectionobserver performance" gets us some articles wherein the potential performance gains of using IntersectionObserver have been tested and quantified. Even if performance was just on-par with an optimized onscroll handler (it's actually better than that), the browser-native Web API is so easy to implement, it's hard to imagine a good reason to choose a different approach.

The Internet is a wonderful place in which to build things these days :)