Skip to content

[BUGFIX] Only follow scrolling for sticky and fixed spotlight targets - #161

Merged
Kanti merged 2 commits into
mainfrom
bugfix/spotlight-scroll-only-for-sticky
Nov 9, 2026
Merged

Kanti merged 2 commits into
mainfrom
bugfix/spotlight-scroll-only-for-sticky

Conversation

@Kanti

@Kanti Kanti commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Follow-up to #159. Its scroll listener recomputed all holes on every scroll frame, on every page, also without any sticky or fixed target: getBoundingClientRect for all targets plus unionRects, which grows quadratically. Measured on a fast machine: about 0.4 ms per frame with 50 targets, 1.2 ms with 300 and 3.7 ms with 1000. The overlay path was not rewritten, but #144 had made scrolling run no script at all, and that was lost. The existing test only counted writes of the path and passed with and without this work.

Implemented behavior

  • Whether a target or one of its ancestors has the computed position fixed or sticky is checked when the targets are selected: in highlight() and in refreshTargets() (resize, mutation, toggle), not while scrolling. Across shadow roots the host counts as ancestor. Ancestors shared by several targets are checked once.
  • Class and style attribute changes of the ancestors of the targets are observed as well, so a header that a script makes fixed while scrolling is followed, as on main. They only recompute the holes when the scroll listener is added or removed.
  • The scroll listener is only registered while such a target exists. Without one, scrolling runs no code again, as after [BUGFIX] Fix spotlight overlay stutter on scroll #144.
  • The rAF coalescing and updateHoles() without measuring the document are unchanged.
  • Known limit: an ancestor that becomes sticky or fixed without a class or style change on one of the ancestors, e.g. by a class on a sibling that a selector depends on, is only noticed with the next selection of the targets (resize, mutation of a target).

Tests

  • spotlight-overlay.test.js (browser): the test for static targets counts the getBoundingClientRect calls while scrolling three times and expects none, instead of counting writes of the path. It fails without the fix (6 calls).
  • The test for sticky and fixed targets passes unchanged.
  • A header that a script makes fixed while scrolling (class toggle, a placeholder keeps its height) stays clickable. This test fails without the observation of the ancestors and passes on main.
  • npm run test: 93 passing, npm run lint

Kanti added 2 commits November 9, 2026 17:20
The scroll listener for sticky and fixed targets recomputed all holes
on every scroll frame, on every page: getBoundingClientRect for all
targets and unionRects, which grows quadratically. This took about
0.4 ms per frame with 50 targets and 3.7 ms with 1000 targets.

Whether a target or one of its ancestors is sticky or fixed is checked
when the targets are selected now. The scroll listener is only added
for such targets, so scrolling runs no code on other pages.
Whether a target depends on scrolling was only checked when the targets
were selected. A header that a script makes fixed while scrolling, by
toggling a class, was static at that time, so its hole stayed in place
and the overlay covered it. On main, the holes followed it.

Class and style changes of the ancestors of the targets are observed
now. They check again whether the holes have to follow the scrolling
and recompute the holes when this changes.
@Kanti
Kanti force-pushed the bugfix/spotlight-scroll-only-for-sticky branch from 1f1f8e9 to f3e7e23 Compare November 9, 2026 16:20
@Kanti
Kanti merged commit 34619ed into main Nov 9, 2026
33 checks passed
Kanti added a commit that referenced this pull request Nov 9, 2026
…164)

Since #161, the holes only followed the scrolling for sticky and fixed
targets and targets in scroll containers. A class or style change of an
ancestor only recomputed the holes when it started or stopped this. So
targets that a script moves while scrolling, e.g. a parallax effect that
sets a transform on a section, or a reveal effect that removes a class
with an offset, kept their old holes, and the overlay covered them.
1.10.4 recomputed the holes on every scroll event and followed them.

A class or style change of an ancestor of the targets recomputes the
holes now, at most once per frame. Pages without such changes still run
no code while scrolling.
@Kanti
Kanti deleted the bugfix/spotlight-scroll-only-for-sticky branch November 9, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant