Scrollspy using Intersection Observer

Photo by Benjamin Elliott on Unsplash
An example of Scrollspy

Intersection Observer

Parameters and options

  1. Intersection root: The region which is to be monitored for intersection with specified nodes. It defaults to the viewport but can also be set to a DOM node.
  2. Root margin: By default, entire root is monitored. But we can specify margins around the root to monitor a larger (or smaller if margins are negative) region than the root.
  3. Threshold: A value between 0 and 1 which specified how much part of the target element(s) should be in the intersection root to trigger the callback function. 0 means as soon as even one pixel enters the region or as soon as the entire node leaves the region. 1 means that the entire node should be in the region. Multiple values can be provided in form of an array.
  4. Callback: A function to be called whenever an intersection takes places.
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.time

The technique

  1. Keep the threshold to 0 (the default value). That means whenever the first pixel of any section enters the viewport or the last pixel of any section leaves the root, the callback will be executed.
  2. Intersection root is the entire viewport by default. We want the intersection to be observed at horizontal line. We can do that by adding a margin to the root and reducing the observed area to a horizontal line. To set it exactly middle of the screen, set rootMargin to -50% 0px
  3. Get a list of all the sections in the page and connect them to the intersection observer.
  4. Whenever an intersection occurs, find out which element caused the intersection. To do that, use entry.isIntersecting property.
Scrollspy using Intersection Observer
window.onload = () => {
const sections = document.getElementsByTagName("section");
const label = document.getElementById("section-name");

const observer = new IntersectionObserver((entries) => {
for(const entry of entries)
label.innerHTML =;
rootMargin: "-50% 0px"
for (let i = 0; i < sections.length; i++)




Frontend engineer at GrowthDay | All things Web

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

HTML for Beginners

JavaScript at Uber

Host Gatsby.js site on amazon s3 with AWS CodePipeline

ReactNative Network Configuration

React Lifecycle Methods 101: Adding a Title to Your Page

Handling Static Forms, The Client-side Way

Testing React — an overview

A Simple way to understand RECURSION

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Yash Mahalwal

Yash Mahalwal

Frontend engineer at GrowthDay | All things Web

More from Medium


Creating Ext JS applications with coon.js — Part 4: Using the Service Locator

MithrilJS — An unfamous amazing JavaScript SPA framework

The STAR ‘*’ of React-Router-Dom