Skip to content

Highlight correct TOC entry on page load #2185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 48 additions & 14 deletions src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -1013,20 +1013,54 @@ function setupArticleTocSyncing() {
return;
}

// When the website visitor clicks a link in the TOC, we want that link to be
// highlighted/activated, NOT whichever TOC link the intersection observer
// callback would otherwise highlight, so we turn off the observer and turn it
// back on later.
// Create a boolean variable that allows us to turn off the intersection
// observer (and then later back on). When the website visitor clicks an
// in-page link, we want that entry in the TOC to be highlighted/activated,
// NOT whichever TOC link the intersection observer callback would otherwise
// highlight.
let disableObserver = false;
pageToc.addEventListener("click", (event) => {
disableObserver = true;
const clickedTocLink = tocLinks.find((el) => el.contains(event.target));
activate(clickedTocLink);
setTimeout(() => {
// Give the page ample time to finish scrolling, then re-enable the
// intersection observer.
disableObserver = false;
}, 1000);

/**
* Check the hash portion of the page URL. If it matches an entry in the page
* table of contents, highlight that entry and temporarily disable the
* intersection observer while the page scrolls to the corresponding heading.
*/
function syncTocHash() {
const { hash: pageHash } = window.location;
if (pageHash.length > 1) {
const matchingTocLink = tocLinks.find((link) => link.hash === pageHash);
if (matchingTocLink) {
disableObserver = true;
setTimeout(() => {
// Give the page ample time to finish scrolling, then re-enable the
// intersection observer.
disableObserver = false;
}, 1000);
activate(matchingTocLink);
}
}
}

// When the page loads and when the user clicks an in-page link,
// sync the page's table of contents.
syncTocHash();
// Note we cannot use the "hashchange" event because if the user clicks a hash
// link, scrolls away, then clicks the same hash link again, it will not fire
// the change event (because it's the same hash), but we still want to re-sync
// the table of contents.
window.addEventListener("click", (event) => {
// Match any link because an in-page ("hash link") can occur anywhere on the
// page, not just in the side table of contents (e.g., one section of the
// page linking to another section of the page, also each of the headings
// contains a link to itself).
if (event.target.closest("a")) {
// Defer the sync operation because window.location.hash does not change
// until after the default action for the event has happened (i.e., the
// link click).
setTimeout(() => {
syncTocHash();
}, 0);
}
});

/**
Expand Down Expand Up @@ -1127,7 +1161,7 @@ function setupArticleTocSyncing() {
}

observer = new IntersectionObserver(callback, options);
headingsToTocLinks.keys().forEach((heading) => {
Array.from(headingsToTocLinks.keys()).forEach((heading) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually causing a runtime error in Safari for me. Only the latest versions of Safari support forEach on the map iterator.

observer.observe(heading);
});
}
Expand Down
Loading