javascript 32 lines · 7 steps

Building a scroll spy with IntersectionObserver

Highlight the nav link for whichever section is currently in view, driven by the browser's IntersectionObserver.

Explained by highlit
1function initScrollSpy() {
2 const links = Array.from(document.querySelectorAll('.nav a[href^="#"]'));
3 const sections = links
4 .map((link) => document.querySelector(link.getAttribute('href')))
5 .filter(Boolean);
6 
7 const setActive = (id) => {
8 links.forEach((link) => {
9 link.classList.toggle('is-active', link.getAttribute('href') === `#${id}`);
10 });
11 };
12 
13 const observer = new IntersectionObserver(
14 (entries) => {
15 const visible = entries
16 .filter((entry) => entry.isIntersecting)
17 .sort((a, b) => b.intersectionRatio - a.intersectionRatio);
18 
19 if (visible.length > 0) {
20 setActive(visible[0].target.id);
21 }
22 },
23 {
24 rootMargin: '-40% 0px -55% 0px',
25 threshold: [0, 0.25, 0.5, 0.75, 1],
26 }
27 );
28 
29 sections.forEach((section) => observer.observe(section));
30 
31 return () => observer.disconnect();
32}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1IntersectionObserver lets the browser report visibility changes instead of you polling scroll position on every frame.
  2. 2Sorting entries by intersectionRatio picks the most-visible section when several qualify at once.
  3. 3Returning a disconnect function gives callers a clean way to tear down the observer.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a scroll spy with IntersectionObserver — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code