Scrollspy

Spying on elements in a scrollable container, automatically highlights their related targets based on scroll position to indicate which element is currently active in the viewport.

How it works

Scrollspy spys on elements in a scrollable container and calculate the position of the “spied” elements to determine if a certain element is scrolled into view. If it is, the configured classname will be added to it’s related target element. To make scrollspy work properly, you have to instantiate a Scrollspy instance with three parameters.

  1. An array of target elements you want to highlight when the “spied” element is scrolled into view. The id of “spied” element must be referenced by target element via href attribute. If the natural structure of target elements is a tree, you must construct a nested array to represent the tree before passing it to Scrollspy instance.
  2. “Spied” container, either document.body or user specified element.
  3. A config object, see config section bellow for more details.

Examples

Spy on document.body

Check the live demo.

In this example, headings of the article are “spied” elements, and anchors in the table of content are target elements, and the scrollable container is document.body. What we want is whenever a heading is scrolled into view, highlighting it’s related item in the table of content. The JavaScript code is like bellow:

(function () {
  function appendChildren(root, children) {
    return [].filter
      .call(root.children, (el) => el.nodeName === "LI")
      .forEach((liElem) => {
        const aElem = liElem.querySelector(":scope > a");
        // push the anchor element to target elements array
        children.push(aElem);
        const ulElem = liElem.querySelector(":scope > ul");
        // find anchors and push to target elements array recursively
        if (ulElem) {
          const nextElements = [];
          children.push(nextElements);
          appendChildren(ulElem, nextElements);
        }
      });
  }
  // target elements array
  const tree = [];
  appendChildren(document.getElementById("menu"), tree);
  // tree is a nested array represents the tree structure of all anchors like this:
  // [item1, [item1.1, item1.2, [item1.2.1, item1.2.2]], item2, item3...]
  // the plain item in the target elements array represents a tree node, and the array item represents subtree of it's previous item.
  new kutsu.Scrollspy(tree, document.body, {
    activeClassName: "text-red-500",
  });
})();

Spy on scrollable container

Scrollspy can also spys on user specified scrollable container instead of document.body.

Item 1

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Item 2

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Item 2-1

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Item 2-2

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Item 3

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Item 4

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It’s repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Config

NameTypeDefaultDescription
activeClassNameundefined | stringundefinedClasses added to target element when its referenced element scrolls into view.
offsetundefined | number | (() => number)0Offset from the top of the viewport when calculating active element. Use a function for dynamic values (e.g., fixed navbar height).

Methods

MethodDescription
refreshRefresh the state. If the structure of target elements changed, this method must be called with the updated target elements array.