This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Web-Dev-Lesson-Test/00-Lesson-Site/src/components/Post/FloatingTOC.astro
MangoPig f9a669ae69 Yes
2025-12-08 23:32:49 +00:00

66 lines
1.7 KiB
Plaintext

---
// Path: src/components/Post/FloatingTOC.astro
import styles from "./FloatingTOC.module.scss";
---
<nav class={styles.toc} aria-label="Table of Contents">
<ul id="toc-list"></ul>
</nav>
<script>
function generateTOC() {
const content = document.getElementById("lesson-container");
const list = document.getElementById("toc-list");
if (list) list.innerHTML = "";
if (!content || !list) return;
const targets = content.querySelectorAll("[data-toc]");
targets.forEach((el, index) => {
if (!el.id) el.id = `toc-item-${index}`;
const label = el.getAttribute("data-toc");
const level = el.getAttribute("data-toc-level") || "1";
const li = document.createElement("li");
const a = document.createElement("a");
// Add data attribute for CSS to target specific lengths
li.setAttribute("data-level", level);
a.href = `#${el.id}`;
// No Icon span, just the text which we will hide via CSS
a.innerHTML = `<span class="toc-text">${label}</span>`;
a.addEventListener("click", (e) => {
e.preventDefault();
el.scrollIntoView({ behavior: "smooth", block: "center" });
});
li.appendChild(a);
list.appendChild(li);
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Clear all active classes
document.querySelectorAll("#toc-list a").forEach((link) => link.classList.remove("active"));
// Set current active
a.classList.add("active");
}
});
},
// Tweak: Use a 1px line exactly in the vertical center of the screen
{ rootMargin: "-50% 0px -50% 0px", threshold: 0 }
);
observer.observe(el);
});
}
generateTOC();
document.addEventListener("astro:after-swap", generateTOC);
</script>