Home / Website Sections / Scrollspy Table of Contents

Scrollspy Table of Contents in JavaScript, Free with Live Demo

Free scrollspy table of contents in plain JavaScript. Builds the TOC from your headings, highlights the section you are reading, shows progress and scrolls smoothly.

Open live demoDownload HTML fileView code on GitHub
Scrollspy Table of Contents JavaScript project: an automatic table of contents that highlights the section you are reading

Runs on: IntersectionObserver. Works in all modern browsers.

What is the Scrollspy Table of Contents?

A table of contents that builds itself from the headings on the page and highlights the section you are reading as you scroll.

It includes nested items, a progress bar, smooth scrolling and focus handling, and it uses no scroll listener for the highlight.

Good for

  • Documentation sites
  • Long blog posts and guides
  • Legal and policy pages
  • Course lessons

What this project does

How it works

  1. Build from headingsThe script finds every h3 and h4, gives each a clean id and writes the list of links.
  2. Watch a reading zonerootMargin shrinks the viewport to its top 30 percent. The heading that enters that zone is the current section.
  3. Scroll and focusClicking a link scrolls smoothly and moves keyboard focus to the heading, so screen readers follow too.

The key JavaScript

This is the heart of the project. The full file has the rest, including the screen layout and error handling.

const io = new IntersectionObserver((entries) => {
  const visible = entries.filter((e) => e.isIntersecting);
  if (visible[0]) highlight(visible[0].target.id);
}, { rootMargin: "0px 0px -70% 0px" }); // top 30% of the screen

document.querySelectorAll("h2, h3").forEach((h) => {
  h.id ||= h.textContent.toLowerCase().replace(/[^a-z0-9]+/g, "-");
  io.observe(h);
});

How to use it

  1. Click Download HTML file above.
  2. Open the file in a code editor, like VS Code.
  3. Run it from a local server with npx serve . so the camera, microphone and AI features are allowed.
  4. Change the text and colors, then upload it to GitHub Pages, Netlify or your own site. It is one file with no build step.

Questions people ask

Why IntersectionObserver instead of a scroll listener?

The browser does the work and only tells you when something changes, which is cheaper and smoother.

How do I change when a section counts as active?

Change the rootMargin. -70% at the bottom means the heading must reach the top 30 percent of the screen.

Does it work on the whole page?

Yes. Remove the root option and it watches the browser window.

More Website Sections projects