Scrollspy for mobile by Intersection Observer

This is a translation of Japanese article "Intersection Observerによるモバイル用スクロールスパイ".

I was impressed by Intersection Observer を用いた要素出現検出の最適化(“Optimization of detect of element appearance by Intersection Observer”. You can see similar article in English here: Intersection Observer API) by Jxck. And then, I implemented scrollspy for mobile by using it(Firefox works the best. Safari also does fine):

Intersection Observer

Intersection Observer primarily provides a advantage that we can reduce need to watch scroll events. Lazy loading of images seems thought as a typical use case that starts loading images just before <img> elements are in view instead of on page loaded.

But there are other cases scroll events are used: page-top-fixed navigation and scrollspy.

Fixed navigation

You might think that “Why don’t you use position: fixed?” as hearing “fixed navigation”. But the navigation here:

  • scrolls as usual at first
  • is fixed at the top of the page when it reached that point

For that, we need to watch scroll events to see if the navigation is at the top of the page or not. Passive Event Listener rather than Intersection Observer solves this problem, but for this once, I used CSS’s position: sticky property.


I implemented scrollspy by Intersection Observer, especially for mobile.

The reason why there are rarely scrollspy in mobile pages is difficulty to put sidebars(navigations are often in them) in mobile’s narrow view port. But headers and footers which are always on are considered acceptable. It was my idea to use the header area for scrollspy.

Purposes of scrollspy are:

  • showing navigation for the page and
  • showing current position in the page

For this time example, I focused on the latter purpose and chose the way to change shown text in the header on changing current location. For the former purpose, I solved that by making the header expandable by tap.

And there is one more advantage of Intersection Observer. Many of scrollspy implementations follow steps below on changing current position:

  1. Make all items in the navigation “inactive”
  2. Then, make the item referring current position “active”

But using Intersection Observer, I could make only needed item inactive and reduce the loop which iterates over all items.

Firefox CSS implementations

I was surprised at that Firefox implements many newer CSS features: smooth scroll, custom properties, position: sticky, text-decoration-style etc.

Smooth scroll is especially my favorite. When we implement smooth scroll by JavaScript, the target of :target pseudo-class doesn’t change as well as location(address bar)’s fragment part doesn’t automatically. But we can style pages using :target when we do it by browser native smooth scroll.