Skip to content

Commit

Permalink
Improve table of contents intersection observer (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
delucis authored May 17, 2023
1 parent a91191e commit 4460e55
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-trains-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/starlight": patch
---

Fix table of contents intersection observer for all possible viewport sizes.
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const toc = generateToC(Astro.props.headings, config.tableOfContents);
}

constructor() {
super();
super({ smallViewport: true });
const details = this.querySelector('details');
if (!details) return;
const closeToC = () => {
Expand Down
30 changes: 24 additions & 6 deletions packages/starlight/components/TableOfContents/starlight-toc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class StarlightTOC extends HTMLElement {
this._current = link;
}

constructor() {
constructor({ smallViewport = false } = {}) {
super();

/** All the links in the table of contents. */
Expand Down Expand Up @@ -68,17 +68,35 @@ export class StarlightTOC extends HTMLElement {
}
};

const headingsObserver = new IntersectionObserver(setCurrent, {
rootMargin: '-10% 0% -85%',
});

// Observe elements with an `id` (most likely headings) and their siblings.
// Also observe direct children of `.content` to include elements before
// the first heading.
const toObserve = document.querySelectorAll(
'main [id], main [id] ~ *, main .content > *'
);
toObserve.forEach((h) => headingsObserver.observe(h));
/** Start intersections at nav height + 2rem padding. */
const top = (smallViewport ? 104 : 64) + 32;
/** End intersections 1.5rem later. */
const bottom = top + 24;

let observer: IntersectionObserver | undefined;
function observe() {
if (observer) observer.disconnect();
const height = document.documentElement.clientHeight;
const rootMargin = `-${top}px 0% ${bottom - height}px`;
observer = new IntersectionObserver(setCurrent, { rootMargin });
toObserve.forEach((h) => observer!.observe(h));
}
observe();

const onIdle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
let timeout: NodeJS.Timeout;
window.addEventListener('resize', () => {
// Disable intersection observer while window is resizing.
if (observer) observer.disconnect();
clearTimeout(timeout);
timeout = setTimeout(() => onIdle(observe), 200);
});
}
}

Expand Down

0 comments on commit 4460e55

Please sign in to comment.