Skip to content
Merged
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
90 changes: 32 additions & 58 deletions assets/js/highlightHeadline.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,74 +15,48 @@ function initHighlightHeadline() {
const markTocItemInactive = (a) => {return a.removeAttribute('data-current');};
const getTocLinkFromHeading = (h) => {return document.querySelector(`.o-aside__toc a[href="${windowPath}#${encodeURIComponent(h.id).replace(/%\w\w/g, match => match.toLowerCase())}"]`);}

const getDocHeight = () => Math.floor(document.body.clientHeight);
let scrollDebounce;

const visibleHeadings = new Set();
let resizeDebounce;
let currentObserver;
let height = getDocHeight();

function beginObservation(docHeight) {
const observer = new IntersectionObserver(
(entries) => {
if (!isAsideActive()) {
return;
}
function updateActiveHeading() {
if (!isAsideActive()) {
return;
}

entries.forEach((entry) => {
if (entry.isIntersecting) {
visibleHeadings.add(entry.target);
} else {
visibleHeadings.delete(entry.target);
}
});
let currentHeading = null;

// Sort visible (intersecting) headings by inverted order of appearance, then grab the first item (i.e. last visible heading)
const lastVisible = Array.from(visibleHeadings.values()).sort((a, b) => headings.indexOf(b) - headings.indexOf(a))[0];
if (!lastVisible) {
return;
}
for (let i = 0; i < headings.length; i++) {
const heading = headings[i];

headings.forEach((heading) => {
// If it's the last visible item, mark it to make it stand out, else, revert to the default style
// Find the link in the TOC list matching the heading in this list of heading elements
const tocLink = getTocLinkFromHeading(heading);
if (heading === lastVisible) {
if(tocLink){
markTocItemActive(tocLink);
}
} else {
if(tocLink){
markTocItemInactive(tocLink);
}
}
});
},
{
rootMargin: `${docHeight}px 0px -90% 0px`,
threshold: 1, // Only considered intersecting if all the pixels are inside the intersection area
// Add the scroll-padding-top defined in styles.scss + 1px to trigger the detection
if (heading.offsetTop <= window.pageYOffset + 81) {
currentHeading = heading;
} else {
break;
}
);

headings.forEach((heading) => observer.observe(heading));
}

return observer;
headings.forEach((heading) => {
const tocLink = getTocLinkFromHeading(heading);
if (tocLink) {
if (heading === currentHeading) {
markTocItemActive(tocLink);
} else {
markTocItemInactive(tocLink);
}
}
});
}

currentObserver = beginObservation(height); // Start the intersection observer
updateActiveHeading();

window.addEventListener('scroll', () => {
clearTimeout(scrollDebounce);
scrollDebounce = setTimeout(updateActiveHeading, 5);
});

// On resize, replace the observer with a new one matching the updated body height, if different
window.addEventListener('resize', () => {
clearTimeout(resizeDebounce);
resizeDebounce = setTimeout(() => {
const heightAfterResize = getDocHeight();
if (height !== heightAfterResize) {
if (currentObserver) {
currentObserver.disconnect();
}
currentObserver = beginObservation(heightAfterResize);
}
}, 200);
clearTimeout(scrollDebounce);
scrollDebounce = setTimeout(updateActiveHeading, 5);
});
}

Expand Down
Loading