95 lines
3.5 KiB
JavaScript
95 lines
3.5 KiB
JavaScript
|
window.addEventListener('DOMContentLoaded', () => {
|
||
|
|
||
|
const tocElement = document.querySelector('.post-toc-wrap');
|
||
|
|
||
|
const navItems = tocElement.querySelectorAll('.post-toc li');
|
||
|
|
||
|
if (navItems.length > 0) {
|
||
|
|
||
|
const sections = [...navItems].map(element => {
|
||
|
var link = element.querySelector('a.nav-link');
|
||
|
// TOC item animation navigate.
|
||
|
link.addEventListener('click', event => {
|
||
|
event.preventDefault();
|
||
|
var target = document.getElementById(event.currentTarget.getAttribute('href').replace('#', ''));
|
||
|
var offset = target.getBoundingClientRect().top + window.scrollY - 70;
|
||
|
window.anime({
|
||
|
targets: document.scrollingElement,
|
||
|
duration: 500,
|
||
|
easing: 'linear',
|
||
|
scrollTop: offset
|
||
|
});
|
||
|
});
|
||
|
return document.getElementById(link.getAttribute('href').replace('#', ''));
|
||
|
});
|
||
|
|
||
|
|
||
|
|
||
|
function activateNavByIndex(target) {
|
||
|
if (target.classList.contains('active-current')) return;
|
||
|
document.querySelectorAll('.post-toc .active').forEach(element => {
|
||
|
element.classList.remove('active', 'active-current');
|
||
|
});
|
||
|
target.classList.add('active', 'active-current');
|
||
|
var parent = target.parentNode;
|
||
|
while (!parent.matches('.post-toc')) {
|
||
|
if (parent.matches('li')) parent.classList.add('active');
|
||
|
parent = parent.parentNode;
|
||
|
}
|
||
|
// Scrolling to center active TOC element if TOC content is taller then viewport.
|
||
|
window.anime({
|
||
|
targets: tocElement,
|
||
|
duration: 200,
|
||
|
easing: 'linear',
|
||
|
scrollTop: tocElement.scrollTop - (tocElement.offsetHeight / 2) + target.getBoundingClientRect().top - tocElement.getBoundingClientRect().top
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function findIndex(entries) {
|
||
|
let index = 0;
|
||
|
let entry = entries[index];
|
||
|
if (entry.boundingClientRect.top > 0) {
|
||
|
index = sections.indexOf(entry.target);
|
||
|
return index === 0 ? 0 : index - 1;
|
||
|
}
|
||
|
for (; index < entries.length; index++) {
|
||
|
if (entries[index].boundingClientRect.top <= 0) {
|
||
|
entry = entries[index];
|
||
|
} else {
|
||
|
return sections.indexOf(entry.target);
|
||
|
}
|
||
|
}
|
||
|
return sections.indexOf(entry.target);
|
||
|
}
|
||
|
|
||
|
function createIntersectionObserver(marginTop) {
|
||
|
|
||
|
marginTop = Math.floor(marginTop + 10000);
|
||
|
let intersectionObserver = new IntersectionObserver((entries, observe) => {
|
||
|
let scrollHeight = document.documentElement.scrollHeight + 100;
|
||
|
if (scrollHeight > marginTop) {
|
||
|
observe.disconnect();
|
||
|
createIntersectionObserver(scrollHeight);
|
||
|
return;
|
||
|
}
|
||
|
let index = findIndex(entries);
|
||
|
activateNavByIndex(navItems[index]);
|
||
|
}, {
|
||
|
rootMargin: marginTop + 'px 0px -100% 0px',
|
||
|
threshold: 0
|
||
|
});
|
||
|
sections.forEach(element => {
|
||
|
element && intersectionObserver.observe(element);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
createIntersectionObserver(document.documentElement.scrollHeight);
|
||
|
|
||
|
} else {
|
||
|
tocElement.innerHTML = '';
|
||
|
}
|
||
|
|
||
|
|
||
|
});
|
||
|
|