1let scrollTimeout;
2
3const listenActive = () => {
4 const elems = document.querySelector(".pagetoc").children;
5 [...elems].forEach((el) => {
6 el.addEventListener("click", (event) => {
7 clearTimeout(scrollTimeout);
8 [...elems].forEach((el) => el.classList.remove("active"));
9 el.classList.add("active");
10 // Prevent scroll updates for a short period
11 scrollTimeout = setTimeout(() => {
12 scrollTimeout = null;
13 }, 100); // Adjust timing as needed
14 });
15 });
16};
17
18const getPagetoc = () =>
19 document.querySelector(".pagetoc") || autoCreatePagetoc();
20
21const autoCreatePagetoc = () => {
22 const main = document.querySelector("#content > main");
23 const content = Object.assign(document.createElement("div"), {
24 className: "content-wrap",
25 });
26 content.append(...main.childNodes);
27 main.prepend(content);
28 main.insertAdjacentHTML(
29 "afterbegin",
30 '<div class="sidetoc"><nav class="pagetoc"></nav></div>',
31 );
32 return document.querySelector(".pagetoc");
33};
34const updateFunction = () => {
35 if (scrollTimeout) return; // Skip updates if within the cooldown period from a click
36 const headers = [...document.getElementsByClassName("header")];
37 const scrolledY = window.scrollY;
38 let lastHeader = null;
39
40 // Find the last header that is above the current scroll position
41 for (let i = headers.length - 1; i >= 0; i--) {
42 if (scrolledY >= headers[i].offsetTop) {
43 lastHeader = headers[i];
44 break;
45 }
46 }
47
48 const pagetocLinks = [...document.querySelector(".pagetoc").children];
49 pagetocLinks.forEach((link) => link.classList.remove("active"));
50
51 if (lastHeader) {
52 const activeLink = pagetocLinks.find(
53 (link) => lastHeader.href === link.href,
54 );
55 if (activeLink) activeLink.classList.add("active");
56 }
57};
58
59document.addEventListener("DOMContentLoaded", () => {
60 const pagetoc = getPagetoc();
61 const headers = [...document.getElementsByClassName("header")];
62
63 const nonH1Headers = headers.filter(
64 (header) => !header.parentElement.tagName.toLowerCase().startsWith("h1"),
65 );
66 const sidetoc = document.querySelector(".sidetoc");
67 const tocContainer = document.querySelector(".toc-container");
68
69 if (nonH1Headers.length === 0) {
70 if (sidetoc) {
71 sidetoc.style.display = "none";
72 }
73 if (tocContainer) {
74 tocContainer.classList.add("no-toc");
75 }
76 return;
77 }
78
79 if (tocContainer) {
80 tocContainer.classList.add("has-toc");
81 }
82
83 const tocTitle = Object.assign(document.createElement("p"), {
84 className: "toc-title",
85 textContent: "On This Page",
86 });
87 pagetoc.appendChild(tocTitle);
88
89 headers.forEach((header) => {
90 const link = Object.assign(document.createElement("a"), {
91 textContent: header.text,
92 href: header.href,
93 className: `pagetoc-${header.parentElement.tagName}`,
94 });
95 pagetoc.appendChild(link);
96 });
97 updateFunction();
98 listenActive();
99 window.addEventListener("scroll", updateFunction);
100});