1let scrollTimeout;
2
3const listenActive = () => {
4 const elems = document.querySelector(".pagetoc").children;
5 [...elems].forEach((el) => {
6 el.addEventListener("click", (_) => {
7 clearTimeout(scrollTimeout);
8 [...elems].forEach((el) => el.classList.remove("active"));
9 el.classList.add("active");
10
11 scrollTimeout = setTimeout(() => {
12 scrollTimeout = null;
13 }, 100);
14 });
15 });
16};
17
18const autoCreatePagetoc = () => {
19 const main = document.querySelector("#content > main");
20 const content = Object.assign(document.createElement("div"), {
21 className: "content-wrap",
22 });
23 content.append(...main.childNodes);
24 main.prepend(content);
25 main.insertAdjacentHTML(
26 "afterbegin",
27 '<div class="toc-container"><nav class="pagetoc"></nav></div>',
28 );
29 return document.querySelector(".pagetoc");
30};
31
32const getPagetoc = () =>
33 document.querySelector(".pagetoc") || autoCreatePagetoc();
34
35const updateFunction = () => {
36 if (scrollTimeout) return;
37
38 const headers = [...document.getElementsByClassName("header")];
39 if (headers.length === 0) return;
40
41 const threshold = 100;
42 let activeHeader = null;
43
44 for (const header of headers) {
45 const rect = header.getBoundingClientRect();
46
47 if (rect.top <= threshold) {
48 activeHeader = header;
49 }
50 }
51
52 if (!activeHeader && headers.length > 0) {
53 activeHeader = headers[0];
54 }
55
56 const pagetocLinks = [...document.querySelector(".pagetoc").children];
57 pagetocLinks.forEach((link) => link.classList.remove("active"));
58
59 if (activeHeader) {
60 const activeLink = pagetocLinks.find(
61 (link) => activeHeader.href === link.href,
62 );
63 if (activeLink) activeLink.classList.add("active");
64 }
65};
66
67document.addEventListener("DOMContentLoaded", () => {
68 const pagetoc = getPagetoc();
69 const headers = [...document.getElementsByClassName("header")];
70
71 const nonH1Headers = headers.filter(
72 (header) => !header.parentElement.tagName.toLowerCase().startsWith("h1"),
73 );
74 const tocContainer = document.querySelector(".toc-container");
75
76 if (nonH1Headers.length === 0) {
77 if (tocContainer) {
78 tocContainer.classList.add("no-toc");
79 }
80 return;
81 }
82
83 if (tocContainer) {
84 tocContainer.classList.add("has-toc");
85 }
86
87 const tocTitle = Object.assign(document.createElement("p"), {
88 className: "toc-title",
89 textContent: "On This Page",
90 });
91
92 pagetoc.appendChild(tocTitle);
93
94 headers.forEach((header) => {
95 const link = Object.assign(document.createElement("a"), {
96 textContent: header.text,
97 href: header.href,
98 className: `pagetoc-${header.parentElement.tagName}`,
99 });
100 pagetoc.appendChild(link);
101 });
102
103 updateFunction();
104 listenActive();
105
106 const pageElement = document.querySelector(".page");
107 if (pageElement) {
108 pageElement.addEventListener("scroll", updateFunction);
109 } else {
110 window.addEventListener("scroll", updateFunction);
111 }
112});