| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- /**
- * Highlights the current section in the table of contents when scrolling.
- */
- export class TocHighlighter {
- constructor(private tocElement: HTMLElement) {}
- highlight() {
- const article = document.querySelector('article');
- if (this.tocElement && article) {
- const headers: HTMLHeadingElement[] = Array.from(
- article.querySelectorAll('h1[id],h2[id],h3[id],h4[id]'),
- );
- window.addEventListener('scroll', (e) => {
- this.highlightCurrentSection(headers);
- });
- this.highlightCurrentSection(headers);
- }
- }
- private highlightCurrentSection(headers: HTMLElement[]) {
- const locationHash = location.hash;
- Array.from(this.tocElement.querySelectorAll('.active')).forEach((el) =>
- el.classList.remove('active'),
- );
- // tslint:disable:prefer-for-of
- for (let i = 0; i < headers.length; i++) {
- const header = headers[i];
- const nextHeader = headers[i + 1];
- const id = header.getAttribute('id') as string;
- if (
- !nextHeader ||
- (nextHeader && window.scrollY + window.innerHeight - 200 < nextHeader.offsetTop)
- ) {
- this.highlightItem(id);
- return;
- }
- const isCurrentTarget = `#${id}` === locationHash;
- const currentTargetOffset = isCurrentTarget ? 90 : 0;
- if (header.offsetTop + currentTargetOffset >= window.scrollY) {
- this.highlightItem(id);
- return;
- }
- }
- }
- private highlightItem(id: string) {
- const tocItem = this.tocElement.querySelector(`[href="#${id}"]`) as HTMLAnchorElement;
- if (tocItem) {
- tocItem.classList.add('active');
- // ensure the highlighted item is scrolled into view in the TOC menu
- const padding = 12;
- const tocHeight = this.tocElement.offsetHeight;
- const tocScrollTop = this.tocElement.scrollTop;
- const outOfRangeTop = tocItem?.offsetTop < tocScrollTop + padding;
- const outofRangeBottom = tocHeight + tocScrollTop < tocItem.offsetTop + padding;
- if (outOfRangeTop) {
- // console.log('¬TOP');
- this.tocElement.scrollTo({ top: tocItem.offsetTop - tocItem.offsetHeight - padding });
- }
- if (outofRangeBottom) {
- // console.log('$BOTTOm');
- const delta = tocItem.offsetTop - (tocHeight + tocScrollTop);
- this.tocElement.scrollTo({ top: tocScrollTop + delta + tocItem.offsetHeight + padding });
- }
- }
- }
- }
|