scrollSpy.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. /* eslint-disable */
  8. (function scrollSpy() {
  9. var OFFSET = 10;
  10. var timer;
  11. var headingsCache;
  12. var findHeadings = function findHeadings() {
  13. return headingsCache || document.querySelectorAll('.toc-headings > li > a');
  14. };
  15. var onScroll = function onScroll() {
  16. if (timer) {
  17. // throttle
  18. return;
  19. }
  20. timer = setTimeout(function () {
  21. timer = null;
  22. var activeNavFound = false;
  23. var headings = findHeadings(); // toc nav anchors
  24. /**
  25. * On every call, try to find header right after <-- next header
  26. * the one whose content is on the current screen <-- highlight this
  27. */
  28. for (var i = 0; i < headings.length; i++) {
  29. // headings[i] is current element
  30. // if an element is already active, then current element is not active
  31. // if no element is already active, then current element is active
  32. var currNavActive = !activeNavFound;
  33. /**
  34. * Enter the following check up only when an active nav header is not yet found
  35. * Then, check the bounding rectangle of the next header
  36. * The headers that are scrolled passed will have negative bounding rect top
  37. * So the first one with positive bounding rect top will be the nearest next header
  38. */
  39. if (currNavActive && i < headings.length - 1) {
  40. var heading = headings[i + 1];
  41. var next = decodeURIComponent(heading.href.split('#')[1]);
  42. var nextHeader = document.getElementById(next);
  43. if (nextHeader) {
  44. var top = nextHeader.getBoundingClientRect().top;
  45. currNavActive = top > OFFSET;
  46. } else {
  47. console.error('Can not find header element', {
  48. id: next,
  49. heading: heading,
  50. });
  51. }
  52. }
  53. /**
  54. * Stop searching once a first such header is found,
  55. * this makes sure the highlighted header is the most current one
  56. */
  57. if (currNavActive) {
  58. activeNavFound = true;
  59. headings[i].classList.add('active');
  60. } else {
  61. headings[i].classList.remove('active');
  62. }
  63. }
  64. }, 100);
  65. };
  66. document.addEventListener('scroll', onScroll);
  67. document.addEventListener('resize', onScroll);
  68. document.addEventListener('DOMContentLoaded', function () {
  69. // Cache the headings once the page has fully loaded.
  70. headingsCache = findHeadings();
  71. onScroll();
  72. });
  73. })();