sites: scroll-spy tracks scroll position, fires the last section on bottom
The IntersectionObserver-based spy only fired when a section entered a narrow band 40–45% down the viewport. Short sections at the tail of a page — Operator key and Roadmap on theseus.x/extensions, whatever came last on the other pages — never made it into that band because the page runs out of scroll before their top crosses 40%, so their sidebar links stayed dim no matter how far the reader scrolled. Rewrote the spy across every page with a sidebar (theseus.x home / plug-ins / extensions and sirius.x home / tld / theseus / docs / admin / brand): it listens to window scroll, picks the last section whose top has crossed 35% of the viewport, and — when the window is scrolled to within 4px of the bottom — snaps to the final section regardless of where its top is. Every link in the rail can now activate. theseus.x/extensions also had the spy IIFE bundled with the manifest loader in one <script> block near the middle of the page, so at run time the parser hadn't yet reached the last three sections and only Bundled/Community/Build were ever registered. Moved the spy to its own <script> just before </body> so it sees every section that ships.
This commit is contained in:
parent
0e8e1949df
commit
172d16e84b
3 changed files with 686 additions and 655 deletions
|
|
@ -260,23 +260,6 @@
|
||||||
</div>`).join("");
|
</div>`).join("");
|
||||||
} catch (e) { box.innerHTML = '<div class="ext" style="color:var(--dim)">Catalog unavailable right now.</div>'; }
|
} catch (e) { box.innerHTML = '<div class="ext" style="color:var(--dim)">Catalog unavailable right now.</div>'; }
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Sidenav scroll-spy: highlight the section currently in view.
|
|
||||||
(() => {
|
|
||||||
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
|
||||||
if (!links.length) return;
|
|
||||||
const map = new Map();
|
|
||||||
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); });
|
|
||||||
const io = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach((en) => {
|
|
||||||
if (!en.isIntersecting) return;
|
|
||||||
const a = map.get(en.target); if (!a) return;
|
|
||||||
links.forEach((l) => l.classList.remove("active"));
|
|
||||||
a.classList.add("active");
|
|
||||||
});
|
|
||||||
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 });
|
|
||||||
map.forEach((_, el) => io.observe(el));
|
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section id="updates">
|
<section id="updates">
|
||||||
|
|
@ -318,5 +301,33 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Sidenav scroll-spy: highlight the section currently in view.
|
||||||
|
// Uses scroll-position tracking so short sections at the end of the page
|
||||||
|
// still activate their link when the reader scrolls to them.
|
||||||
|
(() => {
|
||||||
|
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
||||||
|
if (!links.length) return;
|
||||||
|
const entries = [];
|
||||||
|
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
|
||||||
|
if (!entries.length) return;
|
||||||
|
const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
|
||||||
|
const update = () => {
|
||||||
|
const doc = document.documentElement;
|
||||||
|
const scrollBottom = window.scrollY + window.innerHeight;
|
||||||
|
if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
|
||||||
|
const line = window.scrollY + window.innerHeight * 0.35;
|
||||||
|
let active = entries[0].link;
|
||||||
|
for (const e of entries) {
|
||||||
|
if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
setActive(active);
|
||||||
|
};
|
||||||
|
window.addEventListener("scroll", update, { passive: true });
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
update();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
32
index.html
32
index.html
|
|
@ -375,20 +375,30 @@ Expected:
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Sidenav scroll-spy: highlight the section currently in view.
|
// Sidenav scroll-spy: highlight the section currently in view.
|
||||||
|
// Uses scroll-position tracking so short sections at the end of the page
|
||||||
|
// still activate their link when the reader scrolls to them.
|
||||||
(() => {
|
(() => {
|
||||||
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
||||||
if (!links.length) return;
|
if (!links.length) return;
|
||||||
const map = new Map();
|
const entries = [];
|
||||||
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); });
|
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
|
||||||
const io = new IntersectionObserver((entries) => {
|
if (!entries.length) return;
|
||||||
entries.forEach((en) => {
|
const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
|
||||||
if (!en.isIntersecting) return;
|
const update = () => {
|
||||||
const a = map.get(en.target); if (!a) return;
|
const doc = document.documentElement;
|
||||||
links.forEach((l) => l.classList.remove("active"));
|
const scrollBottom = window.scrollY + window.innerHeight;
|
||||||
a.classList.add("active");
|
if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
|
||||||
});
|
const line = window.scrollY + window.innerHeight * 0.35;
|
||||||
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 });
|
let active = entries[0].link;
|
||||||
map.forEach((_, el) => io.observe(el));
|
for (const e of entries) {
|
||||||
|
if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
setActive(active);
|
||||||
|
};
|
||||||
|
window.addEventListener("scroll", update, { passive: true });
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
update();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -223,20 +223,30 @@
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Sidenav scroll-spy: highlight the section currently in view.
|
// Sidenav scroll-spy: highlight the section currently in view.
|
||||||
|
// Uses scroll-position tracking so short sections at the end of the page
|
||||||
|
// still activate their link when the reader scrolls to them.
|
||||||
(() => {
|
(() => {
|
||||||
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
const links = document.querySelectorAll(".sidenav a[href^='#']");
|
||||||
if (!links.length) return;
|
if (!links.length) return;
|
||||||
const map = new Map();
|
const entries = [];
|
||||||
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); });
|
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
|
||||||
const io = new IntersectionObserver((entries) => {
|
if (!entries.length) return;
|
||||||
entries.forEach((en) => {
|
const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
|
||||||
if (!en.isIntersecting) return;
|
const update = () => {
|
||||||
const a = map.get(en.target); if (!a) return;
|
const doc = document.documentElement;
|
||||||
links.forEach((l) => l.classList.remove("active"));
|
const scrollBottom = window.scrollY + window.innerHeight;
|
||||||
a.classList.add("active");
|
if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
|
||||||
});
|
const line = window.scrollY + window.innerHeight * 0.35;
|
||||||
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 });
|
let active = entries[0].link;
|
||||||
map.forEach((_, el) => io.observe(el));
|
for (const e of entries) {
|
||||||
|
if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
setActive(active);
|
||||||
|
};
|
||||||
|
window.addEventListener("scroll", update, { passive: true });
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
update();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue