Skip to Content
Course content

86: Working with the Intersection Observer API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're hiring a security guard to watch a specific gate. You have two choices. You could stand there yourself and stare at the gate every single second of the day, constantly asking, "Is someone there yet? How about now? Now?" That's exhausting, and you can't get anything else done. Or, you can tell the guard, "Hey, I'm going to go grab some coffee. Just ring this bell the moment someone actually walks through that gate."

In the world of JavaScript, the first approach is like attaching a scroll event listener to the window. It fires hundreds of times a second, forcing the browser to recalculate positions constantly, which usually leads to "jank" or laggy scrolling. The second approach—the security guard—is exactly how the Intersection Observer API works. It lets you asynchronously observe changes in the intersection of a target element with an ancestor element or a top-level document's viewport.

Here is how that analogy maps to the code:

  • The Gate: This is the target element you want to watch (like a footer or an image).
  • The Security Guard: This is the IntersectionObserver instance.
  • The Bell: This is the callback function that runs only when the target enters or leaves the view.
  • The Instructions: These are the options (like threshold) where you tell the guard, "Don't ring the bell until at least half the person is through the gate."

Stop polling and start observing

I've seen way too many developers struggle with getBoundingClientRect() inside a scroll loop. It's a performance killer. Instead, let's look at how we actually implement an observer. Suppose we have a series of "feature cards" on a landing page that we want to fade in as the user scrolls down to them.

const observerOptions = {
  root: null, // Use the browser viewport as the container
  rootMargin: '0px', 
  threshold: 0.2 // Trigger when 20% of the element is visible
};

const observerCallback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Element is visible!');
      entry.target.classList.add('fade-in-visible');
      
      // Once it's visible, we don't need to watch it anymore
      observer.unobserve(entry.target);
    }
  });
};

const observer = new IntersectionObserver(observerCallback, observerOptions);

// Now we tell the observer which elements to watch
const cards = document.querySelectorAll('.feature-card');
cards.forEach(card => observer.observe(card));

Tuning the trigger point

The options object is where you fine-tune the behavior. I usually spend the most time messing with the threshold. A value of 0 means "trigger the moment a single pixel enters the view," while 1.0 means "trigger only when the entire element is fully visible."

Then there's rootMargin. Think of this as a "buffer zone" around the viewport. If you set rootMargin: '200px', the observer will trigger the callback when the element is still 200 pixels outside the viewport. This is a lifesaver for lazy-loading images; you can start loading the image before the user actually reaches it, so it's already there by the time they arrive. It makes the site feel much snappier.

Cleaning up your observers

One thing people often forget is that observers stay active until you tell them to stop. If you're building a single-page app where components mount and unmount, you'll end up with memory leaks if you don't clean up. Use observer.unobserve(element) to stop watching a specific item, or observer.disconnect() to shut down the entire observer and stop watching everything.

In the example above, I used observer.unobserve(entry.target) inside the callback. Since we only want the "fade-in" animation to happen once, there's no reason to keep the guard on duty for that specific card once the animation has triggered.




📋 Practical Task

Build a "Scroll-to-Load" Image Gallery

Your task is to create a simple image gallery that implements lazy-loading using the Intersection Observer API. This prevents the browser from downloading images that the user never scrolls down to see.

Requirements:

  • Create an HTML page with at least 5 large image placeholders (use <div> tags with a background color and a height of 400px to simulate images).
  • Give each placeholder a data-src attribute containing a URL to a real image (you can use https://picsum.photos/).
  • Implement an IntersectionObserver that watches these placeholders.
  • When a placeholder enters the viewport (use a threshold of 0.1), change the div into an <img> tag (or change the background-image style) using the URL from data-src.
  • Ensure that once an image is loaded, the observer stops watching that specific element to save resources.
  • Add a rootMargin of 100px so the images start loading slightly before they hit the screen.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.