PageSourceSearch

https://benjitrapp.github.io/js/reading-progress.js

js benjitrapp.github.io collected 2026-10-03 08:42:09 UTC 2,239 bytes, 68 lines download raw bytes

1// Reading progress bar. The element is hidden on desktop/tablet via CSS and
2// only shown inside the slim fixed header on smartphones, so this runs cheaply
3// everywhere but is only visible where it matters.
4(function () {
5  var bar = document.querySelector('.reading-progress > span');
6  if (!bar) return;
7
8  var ticking = false;
9
10  function update() {
11    var doc = document.documentElement;
12    var scrollTop = doc.scrollTop || document.body.scrollTop || 0;
13    var height = doc.scrollHeight - doc.clientHeight;
14    var pct = height > 0 ? (scrollTop / height) * 100 : 0;
15    if (pct < 0) pct = 0;
16    if (pct > 100) pct = 100;
17    bar.style.width = pct + '%';
18    ticking = false;
19  }
20
21  function onScroll() {
22    if (!ticking) {
23      window.requestAnimationFrame(update);
24      ticking = true;
25    }
26  }
27
28  window.addEventListener('scroll', onScroll, { passive: true });
29  window.addEventListener('resize', onScroll);
30  update();
31
32  // Scrubbing: tap or drag along the bar to jump to that point in the page.
33  var track = document.querySelector('.reading-progress');
34  if (track) {
35    var dragging = false;
36
37    function scrubTo(clientX) {
38      var rect = track.getBoundingClientRect();
39      if (rect.width <= 0) return; // hidden (desktop/tablet)
40      var frac = (clientX - rect.left) / rect.width;
41      if (frac < 0) frac = 0;
42      if (frac > 1) frac = 1;
43      var doc = document.documentElement;
44      var height = doc.scrollHeight - doc.clientHeight;
45      // behavior:'instant' overrides the page's `scroll-behavior: smooth`, so the
46      // scrub tracks the finger immediately instead of animating behind it.
47      window.scrollTo({ top: frac * height, behavior: 'instant' });
48      update();
49    }
50
51    track.addEventListener('pointerdown', function (e) {
52      dragging = true;
53      if (track.setPointerCapture) {
54        try { track.setPointerCapture(e.pointerId); } catch (err) {}
55      }
56      scrubTo(e.clientX);
57      e.preventDefault();
58    });
59
60    track.addEventListener('pointermove', function (e) {
61      if (dragging) scrubTo(e.clientX);
62    });
63
64    function endDrag() { dragging = false; }
65    track.addEventListener('pointerup', endDrag);
66    track.addEventListener('pointercancel', endDrag);
67  }
68}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.