Today I Learned

Intersection Observer API

The Intersection Observer API is a highly performant, asynchronous method of detecting when a target element intersects with other elements or the top-level viewport.

Most commonly, it's used to detect if a target element is visible in the viewport or not. That comes in handy if you want to:

  • Lazy-load images (or other content) as a page is scrolled.
  • Implement "infinite scrolling" feeds, like on social media apps.
  • Auto-play videos or audio only when they're on-screen, to reduce confusion and prevent performance issues.
  • Make use of "scroll-spies", like when you're reading a big article or documentation that highlights in a sidebar which section you're currently reading about.

Historically, to get that sort of information you'd need to rely on a sluggish listener on the scroll event and use methods like Element.getBoundingClientRect(), documentElement.clientHeight or window.innerHeight to calculate viewport height and the target elements current location in respect to that height to determine whether it's visible or not.

It was always a pretty poor solution, since it involved a bunch of maths and relying on an event listener that fired hundreds of times per second, resulting in poor performance and being prone to mathematical errors.

Luckily though, it was enough of a thorn in the side of so many developers that the powers-that-be decided to come up with a much better solution: The Intersection Observer API.

Running outside of the main thread, the API was much quicker and took a lot of the headache away from such a common developer problem.

Let's look at a super basic example:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.3 });

document.querySelectorAll('.observe-me').forEach(el => observer.observe(el));

Let's breakdown what's happening here...

The first block defines our observer, which is going to watch the elements passed to it and do some actions based on when any of those are on-screen. Specifically, we're going to add the .visible class to the target element, and then request the observer to stop... er, observing... that element!

That means this example is one directional: Add the class when the element is on-screen then stop observing for changes.

The final line in the code simply get's all the target elements on the page, loops through them, and asks the observe to watch them.

  • threshold: Specifies how much of an element needs to be on-screen before the callback is invoked, ranging from 0 (0%) to 1 (100%).
  • .observe(elem): Tells the observer to watch the specified element.
  • .unobserve(elem): Tells the observer to stop watching the specified element.
  • entry.isIntersecting: Simple boolean for whether the element is on-screen or not.

Official docs: MDN Intersection Observer API


Back to TIL

Let's grow your business,
together

Whether you're after dev work on a brand new site, an updated design, or just have a great business idea you want to get the ball rolling on, I'm here to help. Let's collaborate together!

Start a project