fix: revise TOC logic (#252)

fix bugs in fallback, markVisibleSection; refine rAF and connectedCb
This commit is contained in:
foxton9
2024-12-31 17:27:40 +08:00
committed by GitHub
parent 588a536e87
commit 589c593e53

View File

@@ -75,30 +75,22 @@ class TableOfContents extends HTMLElement {
this.observer = new IntersectionObserver( this.observer = new IntersectionObserver(
this.markVisibleSection, { threshold: 0 } this.markVisibleSection, { threshold: 0 }
); );
} };
markVisibleSection = (entries: IntersectionObserverEntry[]) => { markVisibleSection = (entries: IntersectionObserverEntry[]) => {
requestAnimationFrame(() => {
entries.forEach((entry) => { entries.forEach((entry) => {
const id = entry.target.children[0]?.getAttribute("id"); const id = entry.target.children[0]?.getAttribute("id");
const idx = id ? this.headingIdxMap.get(id) : undefined; const idx = id ? this.headingIdxMap.get(id) : undefined;
if (entry.isIntersecting && this.anchorNavTarget == entry.target)
this.anchorNavTarget = null;
if (idx != undefined) if (idx != undefined)
this.active[idx] = entry.isIntersecting; this.active[idx] = entry.isIntersecting;
if (entry.isIntersecting && this.anchorNavTarget == entry.target.firstChild)
this.anchorNavTarget = null;
}); });
requestAnimationFrame(() => { if (!this.active.includes(true))
if (!document.querySelector(`#toc .${this.visibleClass}`)) {
this.fallback(); this.fallback();
} this.update();
this.toggleActiveHeading();
this.scrollToActiveHeading();
});
});
}; };
toggleActiveHeading = () => { toggleActiveHeading = () => {
@@ -132,7 +124,7 @@ class TableOfContents extends HTMLElement {
if (this.anchorNavTarget || !this.tocEl) return; if (this.anchorNavTarget || !this.tocEl) return;
const activeHeading = const activeHeading =
document.querySelectorAll<HTMLDivElement>("#toc .visible"); document.querySelectorAll<HTMLDivElement>(`#toc .${this.visibleClass}`);
if (!activeHeading.length) return; if (!activeHeading.length) return;
const topmost = activeHeading[0]; const topmost = activeHeading[0];
@@ -153,6 +145,15 @@ class TableOfContents extends HTMLElement {
}); });
}; };
update = () => {
requestAnimationFrame(() => {
this.toggleActiveHeading();
// requestAnimationFrame(() => {
this.scrollToActiveHeading();
// });
});
};
fallback = () => { fallback = () => {
if (!this.sections.length) return; if (!this.sections.length) return;
@@ -165,17 +166,13 @@ class TableOfContents extends HTMLElement {
|| (offsetTop < 0 && offsetBottom > window.innerHeight)) { || (offsetTop < 0 && offsetBottom > window.innerHeight)) {
this.markActiveHeading(i); this.markActiveHeading(i);
} }
else break; else if (offsetTop > window.innerHeight) break;
} }
requestAnimationFrame(() => {
this.toggleActiveHeading();
})
}; };
markActiveHeading = (idx: number)=> { markActiveHeading = (idx: number)=> {
this.active[idx] = true; this.active[idx] = true;
} };
handleAnchorClick = (event: Event) => { handleAnchorClick = (event: Event) => {
const anchor = event const anchor = event
@@ -195,14 +192,19 @@ class TableOfContents extends HTMLElement {
isInRange(value: number, min: number, max: number) { isInRange(value: number, min: number, max: number) {
return min < value && value < max; return min < value && value < max;
} };
connectedCallback() { connectedCallback() {
// wait for the onload animation to finish, which makes the `getBoundingClientRect` return correct values // wait for the onload animation to finish, which makes the `getBoundingClientRect` return correct values
setTimeout(() => { const element = document.querySelector('.prose');
if (element) {
element.addEventListener('animationend', () => {
this.init(); this.init();
}, 250); }, { once: true });
} else {
console.warn('Animation element not found');
} }
};
init() { init() {
this.tocEl = document.getElementById( this.tocEl = document.getElementById(
@@ -221,6 +223,8 @@ class TableOfContents extends HTMLElement {
document.querySelectorAll<HTMLAnchorElement>("#toc a[href^='#']") document.querySelectorAll<HTMLAnchorElement>("#toc a[href^='#']")
); );
if (this.tocEntries.length === 0) return;
this.sections = new Array(this.tocEntries.length); this.sections = new Array(this.tocEntries.length);
this.headings = new Array(this.tocEntries.length); this.headings = new Array(this.tocEntries.length);
for (let i = 0; i < this.tocEntries.length; i++) { for (let i = 0; i < this.tocEntries.length; i++) {
@@ -240,8 +244,8 @@ class TableOfContents extends HTMLElement {
); );
this.fallback(); this.fallback();
this.scrollToActiveHeading(); this.update();
} };
disconnectedCallback() { disconnectedCallback() {
this.sections.forEach((section) => this.sections.forEach((section) =>
@@ -249,7 +253,7 @@ class TableOfContents extends HTMLElement {
); );
this.observer.disconnect(); this.observer.disconnect();
this.tocEl?.removeEventListener("click", this.handleAnchorClick); this.tocEl?.removeEventListener("click", this.handleAnchorClick);
} };
} }
customElements.define("table-of-contents", TableOfContents); customElements.define("table-of-contents", TableOfContents);