Menu Close

Introducing the Intersection Observer API

We often have to watch for the visibility of an element within the window or a parent element.

To make this task easy, the Intersection Observer API is available for us to use to do that.

In this article, we’ll look at how to use the Intersection Observer API to watch for visibility changes for a child element.

Uses for the Intersection Observer API

We can use the Intersection Observer API to watch for visibility changes of one element or 2 elements in relation to each other.

Watching these changes is useful for things like:

  • lazy-loading images when an element becomes visible
  • infinite scrolling
  • checking the visibility of ads
  • run tasks only when an element is visible

Using the Intersection Observer API

To use the Intersection Observer API, we register a callback that runs whenever the visibility of an element changes. This means when it enters or leaves the viewport.

The callback is called whenever the element called the target intersection either the device viewport or a specified element.

The device viewport or another specified element is called the root element or root.

We have to specify the target element’s intersection ratio to indicate how much of the element is visible before the callback will be called.

It’s an array that has numbers ranges from 0 to 1, where 0 is not visible and 1 is completely visible.

For instance, given that we have the following elements generated by the following code:

for (let i = 0; i < 100; i++) {
  const p = document.createElement('p');
  p.innerText = i;
  document.body.append(p);
}

The code above creates p elements with a number as the text content.

We can create an IntersectionObserver object as follows:

const ps = document.querySelectorAll('p');
let options = {
  root: null,
  rootMargin: '0px',
  threshold: [0, 0.25, 0.5, 0.75, 1]
}

let callback = (entries, observer) => {
  entries.forEach(entry => {
    console.log(entry.intersectionRatio > 0)
  });
};

let observer = new IntersectionObserver(callback, options);
ps.forEach(p => {
  observer.observe(p);
});

In the code above, we first create our options object, which has root set to null so that we watch the elements relative to the root element.

We can also specify another element that we want to watch the child elements from.

The rootMargin property is the margin around each element that we want to watch. We can specify this value as we do with the value of the margin CSS property.

The threshold property is an array of numbers for which the visibility of each element is watched and the callback is called. In the example above, we specified that the callback is called when the element being watched is 0% visible, 25% visible, 50% visible, 75% visible, and 100% visible.

Next, we create the IntersectionObserver object with the IntersectionObserver constructor with the callback and options that we created earlier passed in.

Then we can observe each p element that was created earlier by writing:

ps.forEach(p => {
  observer.observe(p);
});

Where ps is:

const ps = document.querySelectorAll('p');

which are the p elements that we created earlier.

Then when we scroll up and down the page, we should see true or false logged from the callback when the intersectionRatio of the element is bigger than 0.

If intersectionRatio is bigger than 0 that means the element is at least partly visible. Otherwise, it’s not visible.

To make observing elements more efficient, we can unobserved them when they’re in view.

For instance, we can do that by adding observer.unobserve(entry.target); into our callback as follows:

let callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0) {
      observer.unobserve(entry.target);
    }
  });
};

We can also manipulate the elements in the callback when the intersectionRatio or any other property changes as follows:

let callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0.75) {
      entry.target.classList.add('highlight');
    } else {
      entry.target.classList.remove('highlight');
    }
  });
};

In the code above, we add the highlight class to the p element when the intersectionRatio is bigger than 0.75. This means the given element is more than 75% visible.

Given that the highlight class has the following CSS:

.highlight {
  color: green;
}

We get that the color of the text is green when the text is more than 75% visible on the page.

Conclusion

The Intersection Observer API makes it easy to see whether an element is visible or not. We can watch for changes in each element that we want to check the visibility of and then do things like add and remove classes or styles accordingly.

We can also unobserve elements that don’t need to be watched if we want to make our code more efficient.

Posted in JavaScript APIs