Created by: stevenbuccini
Editor's note
I have put together a working proof of concept of re-working the Scrollspy API using the Intersection Observer API. This code is still in a very rough state and some edge cases aren't working, but I wanted to push it up to show how such an approach might work and to get feedback from the community as to whether or not it is worth investing more time into this approach. Thanks!
Summary
Currently, the Scrollspy plugin calculates the current scroll position and then uses that data to index into the “correct” bounding rectangle for a given element in the “scroll element”. This approach is known to be buggy.
We propose migrating this functionality to use the Intersection Observer API. This API, which is implemented in all major browsers, proactively notifies us when browser elements become (in)visible within a defined “scroll element”. This allows us to remove the brittle logic surrounding the calculation of scroll height and position, which are always liable to change as the DOM is updated.
Architecture
We maintain a queue of all “visible” elements, which we update in the callback for the Intersection Observer API.
To determine which element to highlight as “active”, we use the following rules:
- If we are the top or bottom of the scroll position, simply highlight the first or last (respectively) element
- If only one element is in our queue, highlight that element
- If two elements are in our queue, highlight the element with the larger height property of the “intersectionRect”, e.g. the element with more actual content visible
- If more than three elements are visible, make the middle element in the queue active
Ideally, we can accomplish this by updating only the calculations in _process()
and by adding metadata in the Scrollspy constructor to support these operations. If successful, this could be a drop-in replacement for the existing implementation
Pros/Cons
Pros
- We can rely on the browser to tell us when items are visible rather than attempting to calculate it ourselves
- The browser proactively notifies us when there’s a change rather than running a callback on every scroll event to determine if a change is needed
- Not reliant on jQuery or other external dependencies
Cons
- The API only notifies us when certain thresholds are crossed making it hard for us to build robust activation logic when there are multiple items on the page.
- The Intersection Observer API uses ratios which makes it hard for us to anticipate all the corner cases that Bootstrap encounters in the real world.
- Despite widespread adoption, the API is still technically a working draft.
TODO
-
Handle edge cases when we are at the top/bottom of a scroll element -
Tune thresholds to better select the active element if >1 elements are visible within the scroll element at the same time (thinking about using a bottom percentage margin on the scroll element to hack this feeling rather than using the built-in thresholds, which rely on ratios and are therefore brittle)