Simple Table-of-Contents Highlighting Stimulus Controller
Jon Sully
1 Minute
Sharing a bit of code to highlight some links!
Just a quick post here to share some code. I wanted to add subtle highlighting (or bolding, really) to my Table of Contents as the reader scrolls through the page:
So after a Google-search found nothing out there, I whipped up an ultra-simple Stimulus controller to do this for me.
First, we need a couple of bindings in the HTML. These two go on the upper-most <section>
of my blog.html.erb
layout:
data-controller="toc-highlight" data-action="scroll@window->toc-highlight#updateHighlight"
But can sort of go anywhere (as long as it’s hierarchically above the TOC itself) since everything operates via a window-level event binding and the following target
. Since my TOC is generated from the Markdown and injected via Redcarpet, I simply add the target
to the parent container like so:
<div data-toc-highlight-target="links"> <%= toc(post).html_safe %> </div>
And that’s it. Now we just need the simple Stimulus code itself:
// app/javascript/controllers/toc_highlight_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["links"] updateHighlight() { const links = [...this.linksTarget.getElementsByTagName("a")] let highlightedLink = null links.forEach(link => { const id = link.getAttribute("href").slice(1) const section = document.getElementById(id) const rect = section.getBoundingClientRect() // Check if the section's top is past half the window height if (rect.top <= window.innerHeight / 2) { highlightedLink = link } }) // If a link is to be highlighted, remove bold from others and highlight this one if (highlightedLink) { links.forEach(link => link.classList.remove("font-bold")) highlightedLink.classList.add("font-bold") } } }
Pretty straightforward! Any time a header scrolls past the half-way mark of the window, it gets bolded!
Feel free to check out this blog post to see it in action.
✌️