Skip to content

Commit 8e3841c

Browse files
committed
Address review comments
1 parent 6370851 commit 8e3841c

File tree

1 file changed

+17
-16
lines changed

1 file changed

+17
-16
lines changed

src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js

+17-16
Original file line numberDiff line numberDiff line change
@@ -991,9 +991,10 @@ async function addTOCScrollSpy() {
991991
const options = {
992992
root: null, // Target the viewport
993993
// Offset the rootMargin slightly so that intersections trigger _before_ headings begin to
994-
// go offscreen
994+
// go offscreen. Need to account for the height of the top menu bar which is sticky, and
995+
// obscures the top part of the viewport.
995996
rootMargin: `${-2 * document.querySelector("header.bd-header").getBoundingClientRect().bottom}px 0px 0px 0px`,
996-
threshold: 0, // Trigger as soon as 1 pixel is visible
997+
threshold: 0, // Trigger as soon as it becomes visible or invisible
997998
};
998999

9991000
const pageToc = document.querySelector("#pst-page-toc-nav");
@@ -1020,7 +1021,8 @@ async function addTOCScrollSpy() {
10201021
tocElement.setAttribute("aria-current", "true");
10211022

10221023
// Travel up the DOM from the requested element, collecting the set of
1023-
// all parent elements that need to be activated
1024+
// all parent elements that need to be activated. These are the collapsible
1025+
// <li> elements that can hold nested child headings.
10241026
const parents = new Set();
10251027
let el = tocElement.parentElement;
10261028
while (el && el !== pageToc) {
@@ -1032,7 +1034,9 @@ async function addTOCScrollSpy() {
10321034

10331035
// Iterate over all child elements of the TOC, deactivating everything
10341036
// that isn't a parent of the active node and activating the parents
1035-
// of the active TOC entry
1037+
// of the active TOC entry. This closes all collapsible <li> elements
1038+
// of which the active element is not a direct descendent, and activates
1039+
// those which are.
10361040
pageToc.querySelectorAll(".toc-entry").forEach((el) => {
10371041
if (parents.has(el)) {
10381042
el.classList.add("active");
@@ -1059,13 +1063,12 @@ async function addTOCScrollSpy() {
10591063
// in the article to TOC elements, along with information about whether they are
10601064
// visible and the order in which they appear in the article.
10611065
const headingState = new Map(
1062-
Array.from(tocLinks).map((el, index) => {
1066+
Array.from(tocLinks).map((el) => {
10631067
return [
10641068
getHeading(el),
10651069
{
10661070
tocElement: el,
10671071
visible: false,
1068-
index: index,
10691072
},
10701073
];
10711074
}),
@@ -1083,25 +1086,23 @@ async function addTOCScrollSpy() {
10831086
headingState.get(entry.target).visible = entry.isIntersecting;
10841087
});
10851088

1086-
// Sort the active headings by the order in which they appear in the TOC.
1087-
const sorted = Array.from(headingState.values())
1088-
.filter(({ visible }) => visible)
1089-
.sort((a, b) => a.index > b.index);
1090-
10911089
// If there are any visible results, activate the one _above_ the first visible
10921090
// heading. This ensures that when a heading scrolls offscreen, the TOC entry
10931091
// for that entry remains highlighted.
10941092
//
10951093
// If the first element is visible, just highlight the first entry in the TOC.
1096-
if (sorted.length > 0) {
1097-
const idx = sorted[0].index;
1098-
activate(tocLinks[idx > 0 ? idx - 1 : 0]);
1094+
const visible = Array.from(headingState.values()).filter(
1095+
({ visible }) => visible,
1096+
);
1097+
if (visible.length > 0) {
1098+
const indexAbove = Math.max(sorted[0].index - 1, 0);
1099+
activate(tocLinks[indexAbove]);
10991100
}
11001101
}
11011102

11021103
const observer = new IntersectionObserver(callback, options);
1103-
tocLinks.forEach((tocElement) => {
1104-
observer.observe(getHeading(tocElement));
1104+
headingState.forEach((_, heading) => {
1105+
observer.observe(heading);
11051106
});
11061107
}
11071108

0 commit comments

Comments
 (0)