123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import { onMounted, onUnmounted, onUpdated } from 'vue';
- export function useActiveSidebarLinks() {
- let rootActiveLink = null;
- let activeLink = null;
- const onScroll = throttleAndDebounce(setActiveLink, 300);
- function setActiveLink() {
- const sidebarLinks = getSidebarLinks();
- const anchors = getAnchors(sidebarLinks);
- for (let i = 0; i < anchors.length; i++) {
- const anchor = anchors[i];
- const nextAnchor = anchors[i + 1];
- const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor);
- if (isActive) {
- history.replaceState(null, document.title, hash ? hash : ' ');
- activateLink(hash);
- return;
- }
- }
- }
- function activateLink(hash) {
- deactiveLink(activeLink);
- deactiveLink(rootActiveLink);
- activeLink = document.querySelector(`.sidebar a[href="${hash}"]`);
- if (!activeLink) {
- return;
- }
- activeLink.classList.add('active');
- // also add active class to parent h2 anchors
- const rootLi = activeLink.closest('.sidebar-links > ul > li');
- if (rootLi && rootLi !== activeLink.parentElement) {
- rootActiveLink = rootLi.querySelector('a');
- rootActiveLink && rootActiveLink.classList.add('active');
- }
- else {
- rootActiveLink = null;
- }
- }
- function deactiveLink(link) {
- link && link.classList.remove('active');
- }
- onMounted(() => {
- setActiveLink();
- window.addEventListener('scroll', onScroll);
- });
- onUpdated(() => {
- // sidebar update means a route change
- activateLink(decodeURIComponent(location.hash));
- });
- onUnmounted(() => {
- window.removeEventListener('scroll', onScroll);
- });
- }
- function getSidebarLinks() {
- return [].slice.call(document.querySelectorAll('.sidebar a.sidebar-link-item'));
- }
- function getAnchors(sidebarLinks) {
- return [].slice
- .call(document.querySelectorAll('.header-anchor'))
- .filter((anchor) => sidebarLinks.some((sidebarLink) => sidebarLink.hash === anchor.hash));
- }
- function getPageOffset() {
- return document.querySelector('.nav-bar').offsetHeight;
- }
- function getAnchorTop(anchor) {
- const pageOffset = getPageOffset();
- return anchor.parentElement.offsetTop - pageOffset - 15;
- }
- function isAnchorActive(index, anchor, nextAnchor) {
- const scrollTop = window.scrollY;
- if (index === 0 && scrollTop === 0) {
- return [true, null];
- }
- if (scrollTop < getAnchorTop(anchor)) {
- return [false, null];
- }
- if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) {
- return [true, decodeURIComponent(anchor.hash)];
- }
- return [false, null];
- }
- function throttleAndDebounce(fn, delay) {
- let timeout;
- let called = false;
- return () => {
- if (timeout) {
- clearTimeout(timeout);
- }
- if (!called) {
- fn();
- called = true;
- setTimeout(() => {
- called = false;
- }, delay);
- }
- else {
- timeout = setTimeout(fn, delay);
- }
- };
- }
|