class OnThisPage extends window.UIHandler { constructor() { super(); // Initialize state tracking this.navOpen = false; // Bind methods this.toggleNav = this.toggleNav.bind(this); // Bind elements first this.bindElements(); if (this.elements.nav) { if(this.elements.toggle){ // Bind click directly this.elements.toggle.addEventListener('click', this.toggleNav); // Bind UIHandler events for escape and outside clicks this.bindEvents(); } // Set up section observer this.setupSectionObserver(); } } bindElements() { const nav = document.querySelector('nav.on-this-page'); if (!nav) return; this.elements = { nav, links: nav.querySelectorAll('a'), sections: Array.from(nav.querySelectorAll('a')) .map(a => { const id = a.getAttribute('href'); return document.querySelector(id); }) .filter(Boolean) }; if(nav.querySelector('button.toggle')){ this.elements.toggle = nav.querySelector('button.toggle'); } } // Override to prevent UIHandler's component event binding bindComponentEvents() { // Intentionally empty } toggleNav(event) { event?.preventDefault(); event?.stopPropagation(); const { nav, toggle } = this.elements; if (!nav || !toggle) return; // Toggle state this.navOpen = !this.navOpen; // Update DOM if (this.navOpen) { nav.classList.add('open'); toggle.setAttribute('aria-label', 'Hide Index'); toggle.setAttribute('aria-expanded', 'true'); this.bindLinkHandlers(); } else { nav.classList.remove('open'); toggle.setAttribute('aria-label', 'Show Index'); toggle.setAttribute('aria-expanded', 'false'); this.cleanupLinkHandlers(); } } bindLinkHandlers() { const { links } = this.elements; links?.forEach(link => { link._boundHandler = () => { this.navOpen = false; this.elements.nav.classList.remove('open'); this.elements.toggle.setAttribute('aria-label', 'Show Index'); this.elements.toggle.setAttribute('aria-expanded', 'false'); this.cleanupLinkHandlers(); }; link.addEventListener('click', link._boundHandler); }); } cleanupLinkHandlers() { const { links } = this.elements; links?.forEach(link => { if (link._boundHandler) { link.removeEventListener('click', link._boundHandler); delete link._boundHandler; } }); } setupSectionObserver() { const { sections } = this.elements; if (!sections?.length) return; this.initializeObserver( 'sections', sections, { rootMargin: '-50% 0% -50% 0%', threshold: 0 }, (entries) => { entries.forEach(entry => { if (!entry.isIntersecting) return; const id = entry.target.id; const link = this.elements.nav?.querySelector(`a[href="#${id}"]`); if (link) { this.updateActiveClasses(link); } }); } ); } updateActiveClasses(activeLink) { const listItem = activeLink.closest('li'); if (!listItem) return; // Remove existing active and adjacent classes const allItems = this.elements.nav.querySelectorAll('li'); allItems.forEach(item => { item.classList.remove('active', 'adj'); }); // Add new classes listItem.classList.add('active'); // Add adjacent classes if (listItem.previousElementSibling) { listItem.previousElementSibling.classList.add('adj'); } if (listItem.nextElementSibling) { listItem.nextElementSibling.classList.add('adj'); } } // Use local state for component active check isComponentActive(componentKey) { if (componentKey === 'nav') { return this.navOpen; } return super.isComponentActive(componentKey); } // UIHandler event handlers handleOutsideClick(event) { if (this.navOpen && !this.elements.nav.contains(event.target)) { this.toggleNav(event); } } handleEscapeKey(event) { if (event.key === 'Escape' && this.navOpen) { this.toggleNav(event); event.preventDefault(); } } cleanup() { this.cleanupLinkHandlers(); super.cleanup(); } } // Initialize document.addEventListener('DOMContentLoaded', () => { if (document.querySelector('nav.on-this-page')) { window.onThisPage = new OnThisPage(); } });