document.addEventListener('DOMContentLoaded', function() { const tocLinks = document.querySelectorAll('#TableOfContents a'); const tocContainer = document.querySelector('.toc'); const sections = new Map(); // Store section elements with their corresponding TOC links let lastActiveSection = null; let scrollTimeout = null; let isScrolling = false; // Build a map of section IDs to their TOC links tocLinks.forEach(link => { const sectionId = decodeURIComponent(link.getAttribute('href').substring(1)); const section = document.getElementById(sectionId); if (section) { sections.set(section, link); } }); // Function to scroll TOC container to keep active item in view const scrollTocToActive = (activeLink) => { if (!tocContainer || !activeLink) return; const containerRect = tocContainer.getBoundingClientRect(); const linkRect = activeLink.getBoundingClientRect(); // Check if the active link is outside the visible area if (linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom) { // Calculate the scroll position to center the active link const scrollTop = activeLink.offsetTop - (containerRect.height / 2) + (linkRect.height / 2); // Use CSS smooth scrolling if available, otherwise use JS if ('scrollBehavior' in document.documentElement.style) { tocContainer.scrollTo({ top: scrollTop, behavior: 'smooth' }); } else { // Fallback for browsers that don't support smooth scrolling tocContainer.scrollTop = scrollTop; } } }; // Function to find the section closest to the center of the viewport const findClosestSectionToCenter = () => { let closestSection = null; let closestDistance = Infinity; const viewportHeight = window.innerHeight; const viewportCenter = viewportHeight / 2; sections.forEach((link, section) => { const rect = section.getBoundingClientRect(); // Calculate the center of the section const sectionCenter = rect.top + (rect.height / 2); // Calculate distance from the center of the viewport const distance = Math.abs(sectionCenter - viewportCenter); // If this section is closer to the center than our current closest if (distance < closestDistance) { closestDistance = distance; closestSection = section; } }); return closestSection; }; // Function to update the active section const updateActiveSection = (activeSection) => { if (!activeSection) return; // Update active state tocLinks.forEach(link => link.classList.remove('active')); const activeLink = sections.get(activeSection); if (activeLink) { activeLink.classList.add('active'); lastActiveSection = activeSection; // Use a small delay before scrolling to prevent rapid changes if (scrollTimeout) { clearTimeout(scrollTimeout); } scrollTimeout = setTimeout(() => { scrollTocToActive(activeLink); }, 100); } }; // Function to handle scroll events const handleScroll = () => { // Clear any pending scroll timeout if (scrollTimeout) { clearTimeout(scrollTimeout); } // Find the section closest to the center of the viewport const closestSection = findClosestSectionToCenter(); // Update the active section updateActiveSection(closestSection); }; // Add scroll event listener with debouncing let scrollDebounceTimer = null; window.addEventListener('scroll', () => { isScrolling = true; // Debounce the scroll event if (scrollDebounceTimer) { clearTimeout(scrollDebounceTimer); } scrollDebounceTimer = setTimeout(() => { isScrolling = false; handleScroll(); }, 100); }); // Initial check to ensure a section is highlighted setTimeout(() => { handleScroll(); }, 500); // Smooth scroll to section when clicking TOC links tocLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetId = decodeURIComponent(link.getAttribute('href').substring(1)); const targetSection = document.getElementById(targetId); if (targetSection) { // Use our custom smooth scrolling function if available if (window.smoothScrollTo) { window.smoothScrollTo(targetSection); } else { // Fallback to standard smooth scrolling targetSection.scrollIntoView({ behavior: 'smooth' }); } // Also update the active section immediately const activeLink = sections.get(targetSection); if (activeLink) { tocLinks.forEach(l => l.classList.remove('active')); activeLink.classList.add('active'); lastActiveSection = targetSection; scrollTocToActive(activeLink); } } }); }); });