Skip to main content Accessibility Feedback

How to detect when an element enters or leaves the viewport with vanilla JavaScript

The Intersection Observer API can be used to observe and element and run a callback function when it enters or leaves the viewport (or another element).

It runs asynchronously, and it’s far more performant than using a scroll event listener. Today and tomorrow, we’re going to look at how the API works. Today, we’ll tackle the basics. Tomorrow, we’ll get into some of the nitty gritty details.

Let’s dig in.

The new IntersectionObserver() constructor

You can use the new IntersectionObserver() constructor to create a new IntersectionObserver object.

Pass in a callback function as an argument. The callback accepts two arguments: entries, an array of the elements that caused the callback function to run, and optionally the observer itself.

// Create a new IntersectionObserver object
let observer = new IntersectionObserver(function (entries, observer) {
	console.log('An intersection happened!');
	console.log(entries);
});

After creating an observer, use the IntersectionObserver.prototype.observe() method to observe an element. Whenever the observed element enters or leaves the viewport, the callback function will run.

// The element to observe
let app = document.querySelector('#app');

// Observe the #app element
observer.observe(app);

Here’s a demo.

The entries array

In the callback function for the IntersectionObserver object, the entries parameter is an array of IntersectionObserverEntry objects, one for each element that caused the callback function to run.

Often, it will only contain one item, even if multiple elements are being observed, but it’s always an array.

Here, we’re observing multiple heading elements. If one leaves the viewport and another enters at the same time, the entries array would contain IntersectionObserverEntry objects for both. But typically, only one observed element will be entering or leaving the viewport when the callback function is triggered.

You can loop through each entry using a for...of loop or any of the Array object methods, like Array.prototype.forEach().

// Create a new IntersectionObserver object
let observer = new IntersectionObserver(function (entries, observer) {
	for (let entry of entries) {
		console.log(entry);
	}
});

// Get the heading elements
let headings = document.querySelectorAll('h2');

// Observe each heading
for (let heading of headings) {
	observer.observe(heading);
}

Each IntersectionObserverEntry object includes a handful of properties.

The IntersectionObserverEntry.prototype.isIntersecting property has a value of true if the element is in the viewport, and false when it’s not. The IntersectionObserverEntry.prototype.target property is the element itself.

// Create a new IntersectionObserver object
let observer = new IntersectionObserver(function (entries, observer) {
	for (let entry of entries) {
		console.log('the element', entry.target);
		console.log('is intersecting', entry.isIntersecting);
	}
});

Here’s another demo.

Tomorrow, we’ll look at more of the IntersectionObserverEntry instance properties.