javascript 31 lines · 9 steps

Building a scroll-driven reading progress bar

A throttled scroll listener measures how far you've read an article and fills a progress bar to match.

Explained by highlit
1const progressBar = document.getElementById('reading-progress');
2const article = document.querySelector('article');
3 
4function updateProgress() {
5 const { top, height } = article.getBoundingClientRect();
6 const viewport = window.innerHeight;
7 const scrolled = Math.min(Math.max(-top, 0), height - viewport);
8 const total = Math.max(height - viewport, 1);
9 const percent = (scrolled / total) * 100;
10 
11 progressBar.style.width = `${percent}%`;
12 progressBar.setAttribute('aria-valuenow', Math.round(percent));
13}
14 
15function throttle(fn, limit) {
16 let queued = false;
17 return function throttled() {
18 if (queued) return;
19 queued = true;
20 requestAnimationFrame(() => {
21 fn();
22 queued = false;
23 });
24 };
25}
26 
27const onScroll = throttle(updateProgress, 100);
28 
29window.addEventListener('scroll', onScroll, { passive: true });
30window.addEventListener('resize', onScroll, { passive: true });
31updateProgress();
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1getBoundingClientRect gives live scroll offsets relative to the viewport without tracking scroll math yourself.
  2. 2Throttling with requestAnimationFrame caps expensive work to once per frame, keeping scroll handlers smooth.
  3. 3Updating aria-valuenow alongside the visual width keeps the progress bar accessible to screen readers.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a scroll-driven reading progress bar — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code