Using IntersectionObserver to Check if Page Scrolled Past Certain Point

When a web page scrolls, that’s a DOM event. I can find out how far a window has scrolled at any time with window.scrollY. I can listen for that event and get that number:

window.addEventListener("scroll", () => {
console.log(window.scrollY)
});


Let’s say I want to know if the user has scrolled down 100px or more. I could test and see by seeing if window.Y > 100. Here I’ll log if we are or aren’t:

window.addEventListener("scroll", () => {
if (window.scrollY < 100) {
console.log("Not past 100px");
} else {
console.log("Past 100px!");
}
});

But this is a bit of an anti-pattern. It’s simple, understandable, and works, but it’s kind of a bad idea. It’s a bad idea because of how often this fires. As a user scrolls down the page, it can easily fire dozens, hundreds, or thousands of time. Every time it does, we have to execute some JavaScript on JavaScript’s all-important single thread. That means more times figuring out this scrolling stuff and less time doing other important things.

There are ways to make this less intensive, and naturally, they are a very good idea. Throttling and Debouncing are good patterns in JavaScript to improve performance. They are slightly different, so here’s an explanation and here’s some demos. The gist of it is that they prevent larger chunks of JavaScript from executing until you want them to.

This is what debouncing looks like.

There’s an even better way though.

There is another native browser feature called IntersectionObserver that allows you to watch an element and it only will execute JavaScript when significant things happen, like when it enters or leaves the viewport.

So here’s the trick: we place a 1px × 1px pixel element on the page and watch it. Here’s the placement:

<div id="pixel-to-watch"></div>
#pixel-to-watch {
position: absolute;
width: 1px;
height: 1px;
top: 100px;
left: 0;
}

Here’s the watching:

let observer = new IntersectionObserver(entries => {
console.log(entries);
if (entries[0].boundingClientRect.y < 0) {
console.log("Past 100px!");
} else {
console.log("Not past 100px");
}
});
observer.observe(document.querySelector("#pixel-to-watch"));

Here’s a demo where I’m watching to scroll past a pixel in order to choose to fixed position a header:

See the Pen

Fixed Header with IntersectionObserver by Chris Coyier (@chriscoyier)
on CodePen.

Click here to build your next great project on Media Temple.

About the Author Chris is a web designer and developer. He writes about all things web at CSS-Tricks, talks about all things web at conferences around the world and on his podcast ShopTalk, and co-founded the web coding playground CodePen. More by this Author