techstack.hadan.de/theme/default/js/tocbot.js
Stephan Hadan 3ab1c0c119
All checks were successful
Preview / build-deploy (push) Successful in 50s
added styling
2025-02-18 12:48:15 +01:00

132 lines
4.1 KiB
JavaScript

(() => {
function debounce(fn, delay = 1000) {
let time = null;
function _debounce(...args) {
if (time !== null) clearTimeout(time);
time = setTimeout(() => fn.apply(this, args), delay);
}
return _debounce;
}
const scrollSmoothOffset = 56;
function updateScroll() {
const heading = document.getElementById(decodeURIComponent(location.hash.replace(/^#/, '')));
if (heading) {
document.scrollingElement.scrollTop = heading.offsetTop - scrollSmoothOffset + 3;
}
}
function preventClickHandle(selector) {
const mdContainer = document.querySelectorAll(selector);
if (mdContainer && mdContainer.length > 0) {
mdContainer.forEach((anchor) => {
anchor.addEventListener('click', (e) => {
e.preventDefault();
location.hash = anchor.getAttribute('href');
updateScroll();
updateAnchor();
tocsCollapse();
});
});
}
}
function tocButton() {
const tocElement = document.querySelector(`a.gototop`);
if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
tocElement.style.opacity = 0.5;
} else {
tocElement.style.opacity = 0;
}
}
function scrollListener(evn) {
const anchors = document.querySelectorAll('markdown-style a.anchor[href*="#"][aria-hidden]');
const scrollTop = evn && evn.target && evn.target.scrollingElement && evn.target.scrollingElement.scrollTop;
let element;
let index = 0;
anchors.forEach((anchor, idx) => {
if (anchor.offsetTop - scrollSmoothOffset < scrollTop || (idx === 0 && anchor.offsetTop > scrollTop)) {
element = anchor;
index = idx;
}
});
tocButton();
if (element) {
const tocElement = document.querySelector(`a.tocs-link[href='${decodeURIComponent(element.hash)}']`);
if (tocElement) {
updateAnchor(tocElement);
tocsCollapse(tocElement);
} else {
const first = document.querySelector('a.tocs-link[href*="#"]');
if (index === 0 && first) {
updateAnchor(first);
tocsCollapse(first);
}
}
}
}
document.addEventListener('scroll', debounce(scrollListener, 30), false);
function updateAnchor(element) {
const anchorContainer = document.querySelectorAll('.tocs aside.inner.toc a.tocs-link');
anchorContainer.forEach((tocanchor) => {
tocanchor.classList.remove('is-active-link');
});
const anchor = element || document.querySelector(`a.tocs-link[href='${decodeURIComponent(location.hash)}']`);
if (anchor) {
anchor.classList.add('is-active-link');
}
}
function tocsCollapse(element) {
const tocContainer = document.querySelector('nav.tocs > aside.inner.toc');
if (element) {
tocContainer.scrollTop = element.offsetTop;
}
const list = document.querySelectorAll('aside.toc ol.tocs-list');
list.forEach((item) => {
item.classList.remove('is-open');
});
if (element && element.nextElementSibling) {
element.nextElementSibling.classList.add('is-open');
}
isOpen(element);
}
function isOpen(element) {
if (!element) {
element = document.querySelector(`a.tocs-link[href='${decodeURIComponent(location.hash)}']`);
}
if (
element &&
element.parentElement &&
element.parentElement.tagName !== 'ASIDE' &&
!element.parentElement.classList.contains('toc')
) {
isOpen(element.parentElement);
if (element.parentElement.classList.contains('is-collapsed')) {
element.parentElement.classList.add('is-open');
}
}
}
preventClickHandle('markdown-style a.anchor[href*="#"][aria-hidden]');
preventClickHandle('.tocs aside.inner.toc a.tocs-link');
function updateSiderBarScroll() {
const siderBar = document.querySelector(".sidebar[role*='navigation']");
const siderAnchor = document.querySelector(".sidebar[role*='navigation'] a[class*='active']");
if (siderAnchor) {
siderBar.scrollTop = siderAnchor.offsetTop;
}
}
const timer = setTimeout(() => {
updateSiderBarScroll();
updateScroll();
updateAnchor();
tocsCollapse();
clearTimeout(timer);
}, 100);
})();