22. 08. 2025 Dennis Orlando Development, Documentation

A Simple Yet Robust “Scrollspy” Implementation

“It’s a 0.5 story point bugfix, how hard could it be?”

With the recent overhaul of our User Guide UI and its new layout, we inadvertently broke a small but useful feature: the local TOC stopped highlighting the currently active section.

Digging into the legacy implementation revealed that this behavior originally came from Bootstrap’s ScrollSpy component, which we had removed along with Bootstrap itself during our CDN cleanup.

Although plenty of drop-in replacements exist, they were either over-engineered, incompatible with our current DOM structure, or simply required additional npm modules (which we ditched).

We decided to rebuild it from scratch, and this short blog post describes our very simple solution.

Sections!

Most text-first HTML documents are organized as hierarchical `<section>`s containing the actual text. Hierarchies like this come with the neat caveat where the “visual space” occupied by a parent section fully encloses its child sections (...I know, that’s standard HTML.)

As an example, here’s one of the rendered User Guide documents:

After implementing a lot of fancy (and complicated) solutions which didn’t work, we formalized the problem and collected 3 main problems that the solution has to solve:

(A) Multiple nested sections often share the same bottom boundary. As such, they all start intersecting the viewport at once in a random order (especially when scrolling upwards).

(B) Multiple sections can be visible simultaneously, but we only want to highlight the one nearest to the top.

(C) Sometimes we only want to highlight a parent section and not its children – but not always.

Eureka!

After playing around with ever-more-complicated and algorithmically painful O(n^2.5) solutions, we realized that all we need is a line at the top of the viewport, and we can use the modern browser’s Intersection Observer API to track which section is intersecting with that line.

That’s it.

Notice how none of the “unwanted” sections intersect the “watchline”, whereas the parent of the current section (“architecture-of-alyvix-nodes”) does.

After mapping the intersecting sections to the corresponding toc-entry and adding an “active” CSS class with one of NetEye UG’s primary colors, we got our TOC back 🙂

These Solutions are Engineered by Humans

Did you find this article interesting? Does it match your skill set? Programming is at the heart of how we develop customized solutions. In fact, we’re currently hiring for roles just like this and others here at Würth Phoenix.

Dennis Orlando

Dennis Orlando

Author

Dennis Orlando

Leave a Reply

Your email address will not be published. Required fields are marked *

Archive