pagenav.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. /* Section bar: shows which section you are reading, and closes the section list after a choice.
  2. * pisg writes <nav id="pisg-nav"><details>...</details></nav>. Without this script the list still
  3. * opens and every link still works; this only adds the "you are here" label. */
  4. (function () {
  5. "use strict";
  6. var nav = document.getElementById("pisg-nav");
  7. if (!nav) return;
  8. var menu = nav.querySelector("details");
  9. var current = document.getElementById("nav-current");
  10. // Both lists (the dropdown and the menu down the left) carry the same links: one target per section.
  11. var links = Array.prototype.slice.call(nav.querySelectorAll(".nav-list a"));
  12. var all = Array.prototype.slice.call(nav.querySelectorAll(".nav-list a, .nav-side-list a"));
  13. var targets = links.map(function (a) { return document.getElementById(a.getAttribute("href").slice(1)); });
  14. var active = -1, ticking = false;
  15. function setActive(i) {
  16. if (i === active) return;
  17. active = i;
  18. all.forEach(function (a) {
  19. var on = links[i] && a.getAttribute("href") === links[i].getAttribute("href");
  20. a.classList.toggle("active", !!on);
  21. if (on) a.setAttribute("aria-current", "true"); else a.removeAttribute("aria-current");
  22. });
  23. if (current && links[i]) current.textContent = links[i].textContent;
  24. }
  25. function update() {
  26. ticking = false;
  27. var side = window.getComputedStyle(nav).position === "fixed"; // the menu down the left takes no height
  28. var line = (side ? 0 : nav.offsetHeight) + 24, i, hit = 0; // just under the sticky bar
  29. for (i = 0; i < targets.length; i++) {
  30. if (targets[i] && targets[i].getBoundingClientRect().top <= line) hit = i;
  31. }
  32. setActive(hit);
  33. }
  34. function onScroll() { if (!ticking) { ticking = true; (window.requestAnimationFrame || setTimeout)(update); } }
  35. function close() { if (menu && menu.open) menu.open = false; }
  36. window.addEventListener("scroll", onScroll, { passive: true });
  37. window.addEventListener("resize", onScroll);
  38. links.forEach(function (a, i) { a.addEventListener("click", function () { setActive(i); close(); }); });
  39. Array.prototype.forEach.call(nav.querySelectorAll(".nav-side-list a"), function (a) {
  40. a.addEventListener("click", function () { setActive(links.map(function (l) { return l.getAttribute("href"); }).indexOf(a.getAttribute("href"))); });
  41. });
  42. document.addEventListener("keydown", function (e) {
  43. if (e.key === "Escape" && menu && menu.open) { menu.open = false; menu.querySelector("summary").focus(); }
  44. });
  45. document.addEventListener("click", function (e) { if (menu && menu.open && !nav.contains(e.target)) close(); });
  46. update();
  47. })();