/** * Modern Mobile Hamburger Menu * * Handles animated hamburger toggle, slide-in panel, * overlay, sub-menu accordions, focus trapping, and accessibility. */ (function () { const hamburgerBtn = document.getElementById('mmenu-btn'); const navPanel = document.getElementById('mobile-navigation'); const overlay = document.getElementById('mobile-menu-overlay'); const closeBtn = document.getElementById('mobile-nav-close'); if (!navPanel || !hamburgerBtn) { return; } // --- Open / Close helpers --- function openMenu() { hamburgerBtn.classList.add('is-active'); hamburgerBtn.setAttribute('aria-expanded', 'true'); navPanel.classList.add('is-open'); if (overlay) overlay.classList.add('is-visible'); document.body.style.overflow = 'hidden'; } function closeMenu() { hamburgerBtn.classList.remove('is-active'); hamburgerBtn.setAttribute('aria-expanded', 'false'); navPanel.classList.remove('is-open'); if (overlay) overlay.classList.remove('is-visible'); document.body.style.overflow = ''; } function toggleMenu() { if (navPanel.classList.contains('is-open')) { closeMenu(); } else { openMenu(); } } // --- Event listeners --- hamburgerBtn.addEventListener('click', function (e) { e.stopPropagation(); toggleMenu(); }); if (closeBtn) { closeBtn.addEventListener('click', closeMenu); } if (overlay) { overlay.addEventListener('click', closeMenu); } // Close on Escape key document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && navPanel.classList.contains('is-open')) { closeMenu(); hamburgerBtn.focus(); } }); // --- Sub-menu accordion toggles --- const chevronSVG = ''; const parentItems = navPanel.querySelectorAll( '.menu-item-has-children, .page_item_has_children' ); parentItems.forEach(function (item) { const toggleBtn = document.createElement('button'); toggleBtn.className = 'submenu-toggle'; toggleBtn.setAttribute('aria-expanded', 'false'); toggleBtn.setAttribute('aria-label', 'Toggle submenu'); toggleBtn.innerHTML = chevronSVG; toggleBtn.setAttribute('tabindex', '0'); // Insert after the link const link = item.querySelector(':scope > a'); if (link && link.nextSibling) { item.insertBefore(toggleBtn, link.nextSibling); } else { item.appendChild(toggleBtn); } toggleBtn.addEventListener('click', function (e) { e.preventDefault(); e.stopPropagation(); const isExpanded = item.classList.contains('is-expanded'); // Close sibling sub-menus at the same level const siblings = item.parentNode.querySelectorAll( ':scope > .menu-item-has-children.is-expanded, :scope > .page_item_has_children.is-expanded' ); siblings.forEach(function (sib) { if (sib !== item) { sib.classList.remove('is-expanded'); const sibBtn = sib.querySelector(':scope > .submenu-toggle'); if (sibBtn) sibBtn.setAttribute('aria-expanded', 'false'); } }); if (isExpanded) { item.classList.remove('is-expanded'); toggleBtn.setAttribute('aria-expanded', 'false'); } else { item.classList.add('is-expanded'); toggleBtn.setAttribute('aria-expanded', 'true'); } }); }); // --- Focus trap inside open nav --- navPanel.addEventListener('keydown', function (e) { if (e.key !== 'Tab') return; const focusable = navPanel.querySelectorAll( 'button, [href], [tabindex]:not([tabindex="-1"])' ); if (!focusable.length) return; const first = focusable[0]; const last = focusable[focusable.length - 1]; if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } }); })();