My Hugo theme had a jQuery-powered dynamic table of contents. I thought it didn’t make much sense to keep the jQuery library (even a small version) if I’m just using it for that. So, out it went, and in went a JavaScript version with Intersection Observer.
This is based on Ben Frain’s version, simplified a bit since we get the table of contents statically embedded.
Code
The full code (in layouts/partials/article.html
) is:
// Intersection Observer Options
var myObserverOptions = {
root: null,
rootMargin: "0px",
threshold: [1],
};
// Each Intersection Observer runs setCurrent
var observeHtags = new IntersectionObserver(setCurrent, myObserverOptions);
// Add IO to all headings
function addIntersectionObserver() {
var allHtags = document.querySelectorAll(".article-entry > h1, .article-entry > h2, .article-entry > h3");
allHtags.forEach(tag => {
observeHtags.observe(tag);
});
}
// runs when the Intersection Observer is sent
function setCurrent(e) {
e.map(i => {
if (i.isIntersecting === true) {
document.querySelector(`a[href="#${i.target.id}"]`).classList.add("active");
} else {
document.querySelector(`a[href="#${i.target.id}"]`).classList.remove("active");
}
})
}
(function setUp() {
addIntersectionObserver();
document.getElementsByClassName('article-toc')[0].style.display = '';
})();
There’s also a Glitch if you want to try it out simplified.
Older code
If you’re curious, the previous version is here.
Overview
Functionally, what happens is roughly:
- All h1, h2, h3 headings from the content are fetched
- An observer is added to them. This fires an event when the element becomes visible or disappears.
- Ad the observers fire, add the class
active
to each table of contents item
This isn’t perfect.
In particular if you’re reading a section where the heading is no longer visible, it doesn’t continue to highlight that section in the table of contents. A workaround could be to dynamically label all elements within the contents and add intersection observer to them. This is a bit annoying, and likely unnecessarily slows things down (constant events going off even for small scrolls).
Also, it doesn’t do the fancy opening / closing of subsections. I don’t really care for that, since my posts aren’t that long.
Does it work? Check out the sample page.
Comments / questions
There's currently no commenting functionality here. If you'd like to comment, please use Mastodon and mention me ( @hi@johnmu.com ) there. Thanks!