123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- const getPageToc = () => document.getElementsByClassName('pagetoc')[0];
- const pageToc = getPageToc();
- const pageTocChildren = [...pageToc.children];
- const headers = [...document.getElementsByClassName('header')];
- // Select highlighted item in ToC when clicking an item
- pageTocChildren.forEach(child => {
- child.addEventHandler('click', () => {
- pageTocChildren.forEach(child => {
- child.classList.remove('active');
- });
- child.classList.add('active');
- });
- });
- /**
- * Test whether a node is in the viewport
- */
- function isInViewport(node) {
- const rect = node.getBoundingClientRect();
- return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
- }
- /**
- * Set a new ToC entry.
- * Clear any previously highlighted ToC items, set the new one,
- * and adjust the ToC scroll position.
- */
- function setTocEntry() {
- let activeEntry;
- const pageTocChildren = [...getPageToc().children];
- // Calculate which header is the current one at the top of screen
- headers.forEach(header => {
- if (window.pageYOffset >= header.offsetTop) {
- activeEntry = header;
- }
- });
- // Update selected item in ToC when scrolling
- pageTocChildren.forEach(child => {
- if (activeEntry.href.localeCompare(child.href) === 0) {
- child.classList.add('active');
- } else {
- child.classList.remove('active');
- }
- });
- let tocEntryForLocation = document.querySelector(`nav a[href="${activeEntry.href}"]`);
- if (tocEntryForLocation) {
- const headingForLocation = document.querySelector(activeEntry.hash);
- if (headingForLocation && isInViewport(headingForLocation)) {
- // Update ToC scroll
- const nav = getPageToc();
- const content = document.querySelector('html');
- if (content.scrollTop !== 0) {
- nav.scrollTo({
- top: tocEntryForLocation.offsetTop - 100,
- left: 0,
- behavior: 'smooth',
- });
- } else {
- nav.scrollTop = 0;
- }
- }
- }
- }
- /**
- * Populate sidebar on load
- */
- window.addEventListener('load', () => {
- // Only create table of contents if there is more than one header on the page
- if (headers.length <= 1) {
- return;
- }
- // Create an entry in the page table of contents for each header in the document
- headers.forEach((header, index) => {
- const link = document.createElement('a');
- // Indent shows hierarchy
- let indent = '0px';
- switch (header.parentElement.tagName) {
- case 'H1':
- indent = '5px';
- break;
- case 'H2':
- indent = '20px';
- break;
- case 'H3':
- indent = '30px';
- break;
- case 'H4':
- indent = '40px';
- break;
- case 'H5':
- indent = '50px';
- break;
- case 'H6':
- indent = '60px';
- break;
- default:
- break;
- }
- let tocEntry;
- if (index == 0) {
- // Create a bolded title for the first element
- tocEntry = document.createElement("strong");
- tocEntry.innerHTML = header.text;
- } else {
- // All other elements are non-bold
- tocEntry = document.createTextNode(header.text);
- }
- link.appendChild(tocEntry);
- link.style.paddingLeft = indent;
- link.href = header.href;
- pageToc.appendChild(link);
- });
- setTocEntry.call();
- });
- // Handle active headers on scroll, if there is more than one header on the page
- if (headers.length > 1) {
- window.addEventListener('scroll', setTocEntry);
- }
|