loading...
Cover image for Who Observes the Intersection Observers?

Who Observes the Intersection Observers?

renascent479 profile image Serena ・3 min read

Opinions expressed here are my own and do not necessarily reflect that of my employer

Here's a common scenario: You're building a website and want your nav to always remain in view. What would you use? A scroll event listener and calculate the nav's relative offset in relation to the scroll offset? Well pull up a chair, there's a few new tricks that we can use instead.

Sticking the landing

position: sticky is amazing. This CSS property allows element's position property to change from a relative/static to a fixed position when it's about to exit the viewport. Prior to browser support, we'd have to use scroll listeners to determine if an element like a nav menu is at the top of the screen, at which we'll add a CSS class to make it fixed. Now we can just throw it into one class and the browser will handle the change between a static position to a fixed one.

/* Old way */
.Nav-menu {
    position: relative;
}

.Nav-menu.is-fixed {
    position: fixed;
    top: 15px;
    box-shadow: 0px 2px 2px 1px rgba(0, 0, 0, .5);
}

/* Modern way */
.Nav-menu {
    position: sticky;
    top: 15px;
}

Unfortunately it doesn't quite meet all of our needs here. We don't have an event or selector when it's active, so we cannot conditionally add and remove styles based on the sticky condition. Like say we wanted to add a drop shadow when the menu is fixed to give the nav more separation from the content as demonstrated below.

I see you, do you see me?


Intersection Observers to the rescue! IntersectionObserver is a relatively new browser API that's part of the Observer family and allows us to set event triggers when an element comes into or out of viewport.

While this may sound the same as traditional scroll event listeners - commonly used in lazy load image libraries - Observers behave asynchronously, greatly improving performance as we're no longer listening to scroll or resize events.

Dan Callahan of Mozilla created a simple demo showing the different states the target element triggers when scrolling through the viewport.

So how does this help us with the sticky nav? Wouldn't the Intersection Observer's exit event never trigger because position: sticky never leaves viewport? Well, yes, if we had placed the observers on the nav itself. Instead we'll use sentinel elements.

No one can hide from my sight


The sentinel element is derived from the concept of a sentinel value - a value inserted in a dataset to trigger the break of an algorithm. In this case, it'll be an empty HTML element that we'll be attaching our observers to.

We'll place the sentinel at the same position as the nav and pass that to the Intersection Observer. In doing so, this element becomes a waypoint for figuring out the relative scroll position.

<nav class="Nav-menu">
    <a href="/home">Home</a>
    <a href="/bio">Bio</a>
    <a href="/contact">Contact</a>
</nav>
<div class="Sentinel Sentinel--navMenu"></div>
<section>
    Cat ipsum dolor sit amet, annoy kitten brother with poking hit you unexpectedly.
</section>

Now we add some intersection observer magic to hook everything up:

const navMenu = document.querySelector('.Nav-menu');
const navMenuSentinel = document.querySelector('.Sentinel--navMenu');

const navMenuObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if(entry.isIntersecting) {
                navMenu.classList.remove('is-sticky');
            } else {
                navMenu.classList.add('is-sticky');
            }
        }
});
navMenuObserver.observe(navMenuSentinel);

Et c'est comme ça

And here's everything put together:

The position: sticky triggers as before. When the invisible sentinel element scrolls off viewport, it triggers the Intersection Observer's callback to add the class to add a drop shadow to the nav.

Now this was a fairly basic, top level example. IntersectionObserver is easy to use and quite powerful. You can get creative with it as shown in our example with the sentinel. There's also a polyfill available for those pesky IE11 issues.

Hope this post gave everyone a nice intro to IntersectionObserver and some cool use cases. There's plenty of articles elsewhere that go more indepth if you're interested. Till next time!

Discussion

pic
Editor guide
Collapse
maestromac profile image
Mac Siri

This is much cleaner and nicer to use. Thanks for sharing!

Also, Widow would probably prefer position: hidden;.

Collapse
equinusocio profile image
Mattia Astorino

Unfortunately safari mobile doesn’t support intersectionObserver. There is also this tut om google web fundamentals that use sentinels:

developers.google.com/web/updates/...

Collapse
ben profile image
Ben Halpern

Hope this post gave everyone a nice intro to IntersectionObserver and some cool use cases.

It was 🙂

Collapse
alahmadiq8 profile image
Mohammad Alahmadi

this was so much fun to read! I'm living for widowmaker reference ;)