A basic Intersection Observer function will run if the element is intersecting, regardless of the scroll direction. This works good for lazy loading images, where we can use the unobserve method to only observe the target once.

In these examples we will setup the observer to only observe when the target is entering or leaving the bottom of the screen or the top of the screen.

Basic Intersection Observer top and bottom

First, let's start with a basic Intersection Observer setup where we will observe each section and add a background color as it enters the viewport from the top and bottom.

We'll set the observer to add the active class when the element is intersecting or above the window, and remove it when it is not.

  const target = document.querySelectorAll(".target-section")
const options = {
threshold: 0.5,
rootMargin: "-15%"
}
function handleIntersection(entries) {
entries.map((entry) => {
console.log(entry.target)
if (entry.isIntersecting) {
entry.target.classList.add('active')
} else {
entry.target.classList.remove('active')
}
});
}
const observer = new IntersectionObserver(handleIntersection, options);
target.forEach(targets => observer.observe(targets));

Example

One

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Two

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Three

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Four

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

The isIntersecting property is a Boolean value that will be true if the target entry element is intersecting with the viewport.

We can change the viewport with the rootMargin property and move the viewport in by 15%. Now the target has to enter the viewport 15% from each side before the isIntersecting will be true. The threshold value specifies the percentage of overlap that we want to observed as the target enters the viewport. For the example, a value of 0.5 means that the callback would be fired after half of the target element has been scrolled into the viewport.

Now let's add some more parameters to the Intersection Observer. Many times you may want to only fire the isIntersecting property when the target is entering or leaving the bottom.

Only Observe at the bottom of the screen

We can use the entry.boundingClientRect method that uses the same return value as the getBoundingClientRect() JavaScript method.

bounding box in viewport

Using the top of the target bounding box we can check if the target is less than the top of the viewport.

const targetTwo = document.querySelectorAll(".target-two");
const optionsTwo = {
rootMargin: "0px 0px -40% 0px"
}
function ioTwo(entries) {
entries.forEach((entry) => {
//console.log(entry.target)
if (entry.isIntersecting || entry.boundingClientRect.top < 0 ) {
entry.target.classList.add('active')
} else {
entry.target.classList.remove('active')
}
});
}
const observerTwo = new IntersectionObserver(ioTwo, optionsTwo);
targetTwo.forEach(targetsTwo => observerTwo.observe(targetsTwo));

Example

One

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Two

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Three

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Four

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

You can use multiple rootMargin(s) so that active is only removed off the screen we can add another Intersection observer with a rootMargin of rootMargin: "0px 0px -22% 0px".

Only Observe at the top of screen

To observe only when the targets are entering or leaving the top of the screen, we can check if the bounding box is below the bottom of the screen using the window.innerHeight property. Also set rootMargin to observe where you need it from the top of the screen.

const targetThree = document.querySelectorAll(".target-three");
const optionsThree = {
rootMargin: "-40% 0px 0px 0px"
}
function ioThree(entries) {
entries.forEach((entry) => {
//console.log(entry.target)
if (entry.isIntersecting || entry.boundingClientRect.top > window.innerHeight) {
entry.target.classList.remove('active')
} else {
entry.target.classList.add('active')
}
});
}
const observerThree = new IntersectionObserver(ioThree, optionsThree);
targetThree.forEach(targetsThree => observerThree.observe(targetsThree));

Example

One

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Two

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Three

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Four

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere quam sit amet pellentesque iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Vivamus id lectus eget nibh fringilla commodo. Mauris bibendum purus nec orci aliquet tincidunt.

Final Thoughts

It takes some playing around with the Intersection Observer to get use-to-it, but it is well worth it. The performance boost from not have to use on scroll listeners and the ability to manage many types of events with this browser API is a win win.

References