let repeatCount = 0;
let cTimeout: NodeJS.Timeout;
const timeoutIntervalSpeed = 10;
let hashName: string | null;
let scollFlag = false;
let activeLink: Element | null = null;
let rootActiveLink: Element | null = null;
let rootClassName = '';
function elementPosition(obj: HTMLElement) {
let curleft = 0, curtop = 0;
curleft = obj.offsetLeft;
curtop = obj.offsetTop;
return { x: curleft, y: curtop };
}
function getSidebarLinks(curRootClassName: string): Array<HTMLAnchorElement> {
return [].slice.call(document.querySelectorAll(`.${curRootClassName} > .step-nav > li.bar-link-item > a`));
}
function getAnchors(sidebarLinks: Array<HTMLAnchorElement>): Array<HTMLAnchorElement> {
return [].slice
.call(document.querySelectorAll('.box-anchor'))
.filter((anchor: HTMLAnchorElement) => sidebarLinks.some((sidebarLink: HTMLAnchorElement) => sidebarLink.hash === anchor.hash));
}
function throttleAndDebounce(fn: () => void, delay: number): () => void {
let timeout: NodeJS.Timeout;
let called = false;
return () => {
if (timeout) {
clearTimeout(timeout);
}
if (!called) {
fn();
called = true;
setTimeout(() => {
called = false;
}, delay);
}
else {
timeout = setTimeout(fn, delay);
}
};
}
function deactiveLink(link: Element | null): void {
link && link.classList.remove('active');
}
export function hightLightFn(curHashName: string | null): void {
const childLength = document.getElementsByClassName('mysidebar')[0].children.length;
for (let i = 0; i < childLength; i++) {
if (document.getElementsByClassName('mysidebar')[0].children[i].classList.value.indexOf('active') > -1) {
document.getElementsByClassName('mysidebar')[0].children[i].classList.remove('active');
}
}
if (curHashName) {
document.getElementById(curHashName)?.classList.add('active');
}
}
function activateLink(hash: string | boolean | null): void {
deactiveLink(activeLink);
deactiveLink(rootActiveLink);
hash
? activeLink = document.querySelector(`${hash}`)
: activeLink = document.querySelector(`.${rootClassName} ul li`);
if (!activeLink) {
return;
}
if (!scollFlag) {
hash && hightLightFn((hash as string).split('#')[1]);
}else {
hightLightFn(hashName);
}
const rootLi = activeLink.closest('.mycontainer > ul > li');
if (rootLi && rootLi !== activeLink.parentElement) {
rootActiveLink = rootLi;
rootActiveLink && rootActiveLink.classList.add('active');
}
else {
rootActiveLink = null;
}
}
function getPageOffset(): number {
return (document.querySelector('.mysidebar ') as HTMLElement).getBoundingClientRect().y;
}
function getAnchorTop(anchor: HTMLAnchorElement): number {
const pageOffset = getPageOffset();
return (anchor.parentElement?.offsetTop as number) - pageOffset - 5;
}
function isAnchorActive(index: number, anchor: HTMLAnchorElement, nextAnchor: HTMLAnchorElement) {
let scrollTop: number;
document.getElementsByClassName('scrollTarget').length
? scrollTop = document.getElementsByClassName('scrollTarget')[0].scrollTop
: scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
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 scrollSmoothly(scrollPos: number, repeatTimes: number, container: HTMLElement): void {
if (repeatCount <= repeatTimes) {
scrollPos > 0
? container.scrollBy(0, timeoutIntervalSpeed)
: container.scrollBy(0, -timeoutIntervalSpeed);
}
else {
repeatCount = 0;
clearTimeout(cTimeout);
history.replaceState(null, '', document.location.pathname + '#' + hashName);
hightLightFn(hashName);
setTimeout(() => {
scollFlag = false;
}, 310);
return ;
}
repeatCount ++;
cTimeout = setTimeout(() => {
scrollSmoothly(scrollPos, repeatTimes, container);
}, 10);
}
export function scrollToControl(elem: HTMLElement, container: HTMLElement): void {
hashName = elem.getAttribute('name');
scollFlag = true;
const tops = container.scrollTop >= 0 ?
container.scrollTop :
-(document.getElementsByClassName('mycontainer')[0] as HTMLElement).offsetTop;
let scrollPos: number = elementPosition(elem).y - tops ;
scrollPos = scrollPos - document.documentElement.scrollTop;
const remainder: number = scrollPos % timeoutIntervalSpeed;
const repeatTimes = Math.abs((scrollPos - remainder) / timeoutIntervalSpeed);
if (scrollPos < 0 && container || elem.getBoundingClientRect().top < container.offsetTop) {
window.scrollBy(0, elem.getBoundingClientRect().top-container.offsetTop-16);
}
scrollSmoothly(scrollPos, repeatTimes, container);
}
export const setActiveLink = (timeId?: string): void => {
if (scollFlag) { return; }
timeId ? rootClassName = timeId : rootClassName = document.getElementsByClassName('mymain')[0].id;
const sidebarLinks = getSidebarLinks(rootClassName);
const anchors = getAnchors(sidebarLinks);
try {
anchors.forEach((index, i)=> {
const anchor: HTMLAnchorElement = anchors[i];
const nextAnchor: HTMLAnchorElement = anchors[i + 1];
const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor);
if (isActive) {
history.replaceState(null, document.title, hash ? hash as string : ' ');
activateLink(hash);
throw Error(hash + '');
}
});
} catch (e) {
}
};
export const onScroll = throttleAndDebounce(setActiveLink, 300);