/** * Header Navigation JavaScript * * This file handles: * - Mobile hamburger menu toggle * - Sticky header behavior * - Smooth scroll to anchors (optional) * - Accessibility features (keyboard navigation, ARIA attributes) * - Body scroll locking when mobile menu is open * * @package ROI_Theme * @since 1.0.0 */ (function() { 'use strict'; /** * Initialize on DOM ready */ function init() { setupMobileMenu(); setupStickyHeader(); setupSmoothScroll(); setupKeyboardNavigation(); } /** * Mobile Menu Functionality */ function setupMobileMenu() { const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); const mobileMenu = document.getElementById('mobile-menu'); const mobileMenuOverlay = document.getElementById('mobile-menu-overlay'); const mobileMenuClose = document.getElementById('mobile-menu-close'); if (!mobileMenuToggle || !mobileMenu || !mobileMenuOverlay) { return; } // Open mobile menu mobileMenuToggle.addEventListener('click', function() { openMobileMenu(); }); // Close mobile menu via close button if (mobileMenuClose) { mobileMenuClose.addEventListener('click', function() { closeMobileMenu(); }); } // Close mobile menu via overlay click mobileMenuOverlay.addEventListener('click', function() { closeMobileMenu(); }); // Close mobile menu on Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && mobileMenu.classList.contains('active')) { closeMobileMenu(); mobileMenuToggle.focus(); } }); // Close mobile menu when clicking a menu link const mobileMenuLinks = mobileMenu.querySelectorAll('a'); mobileMenuLinks.forEach(function(link) { link.addEventListener('click', function() { closeMobileMenu(); }); }); // Handle window resize - close mobile menu if switching to desktop let resizeTimer; window.addEventListener('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if (window.innerWidth >= 768 && mobileMenu.classList.contains('active')) { closeMobileMenu(); } }, 250); }); } /** * Open mobile menu */ function openMobileMenu() { const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); const mobileMenu = document.getElementById('mobile-menu'); const mobileMenuOverlay = document.getElementById('mobile-menu-overlay'); // Add active classes mobileMenu.classList.add('active'); mobileMenuOverlay.classList.add('active'); document.body.classList.add('mobile-menu-open'); // Update ARIA attributes mobileMenuToggle.setAttribute('aria-expanded', 'true'); mobileMenu.setAttribute('aria-hidden', 'false'); mobileMenuOverlay.setAttribute('aria-hidden', 'false'); // Focus trap - focus first menu item const firstMenuItem = mobileMenu.querySelector('a'); if (firstMenuItem) { setTimeout(function() { firstMenuItem.focus(); }, 300); } } /** * Close mobile menu */ function closeMobileMenu() { const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); const mobileMenu = document.getElementById('mobile-menu'); const mobileMenuOverlay = document.getElementById('mobile-menu-overlay'); // Remove active classes mobileMenu.classList.remove('active'); mobileMenuOverlay.classList.remove('active'); document.body.classList.remove('mobile-menu-open'); // Update ARIA attributes mobileMenuToggle.setAttribute('aria-expanded', 'false'); mobileMenu.setAttribute('aria-hidden', 'true'); mobileMenuOverlay.setAttribute('aria-hidden', 'true'); } /** * Sticky Header Behavior */ function setupStickyHeader() { const header = document.getElementById('masthead'); if (!header) { return; } let lastScrollTop = 0; let scrollThreshold = 100; window.addEventListener('scroll', function() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Add/remove scrolled class based on scroll position if (scrollTop > scrollThreshold) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } lastScrollTop = scrollTop; }, { passive: true }); } /** * Smooth Scroll to Anchors (Optional) */ function setupSmoothScroll() { // Check if user prefers reduced motion const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (prefersReducedMotion) { return; } // Get all anchor links const anchorLinks = document.querySelectorAll('a[href^="#"]'); anchorLinks.forEach(function(link) { link.addEventListener('click', function(e) { const href = this.getAttribute('href'); // Skip if href is just "#" if (href === '#') { return; } const target = document.querySelector(href); if (target) { e.preventDefault(); // Get header height for offset const header = document.getElementById('masthead'); const headerHeight = header ? header.offsetHeight : 0; const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerHeight - 20; window.scrollTo({ top: targetPosition, behavior: prefersReducedMotion ? 'auto' : 'smooth' }); // Update URL hash if (history.pushState) { history.pushState(null, null, href); } // Focus target element for accessibility target.setAttribute('tabindex', '-1'); target.focus(); } }); }); } /** * Keyboard Navigation for Menus */ function setupKeyboardNavigation() { const menuItems = document.querySelectorAll('.primary-menu > li, .mobile-primary-menu > li'); menuItems.forEach(function(item) { const link = item.querySelector('a'); const submenu = item.querySelector('.sub-menu'); if (!link || !submenu) { return; } // Open submenu on Enter/Space link.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { if (submenu) { e.preventDefault(); toggleSubmenu(item, submenu); } } // Close submenu on Escape if (e.key === 'Escape') { closeSubmenu(item, submenu); link.focus(); } }); // Close submenu when focus leaves const submenuLinks = submenu.querySelectorAll('a'); if (submenuLinks.length > 0) { const lastSubmenuLink = submenuLinks[submenuLinks.length - 1]; lastSubmenuLink.addEventListener('keydown', function(e) { if (e.key === 'Tab' && !e.shiftKey) { closeSubmenu(item, submenu); } }); } }); } /** * Toggle submenu visibility */ function toggleSubmenu(item, submenu) { const isExpanded = item.classList.contains('submenu-open'); if (isExpanded) { closeSubmenu(item, submenu); } else { openSubmenu(item, submenu); } } /** * Open submenu */ function openSubmenu(item, submenu) { item.classList.add('submenu-open'); submenu.setAttribute('aria-hidden', 'false'); const firstLink = submenu.querySelector('a'); if (firstLink) { firstLink.focus(); } } /** * Close submenu */ function closeSubmenu(item, submenu) { item.classList.remove('submenu-open'); submenu.setAttribute('aria-hidden', 'true'); } /** * Trap focus within mobile menu when open */ function setupFocusTrap() { const mobileMenu = document.getElementById('mobile-menu'); if (!mobileMenu) { return; } document.addEventListener('keydown', function(e) { if (!mobileMenu.classList.contains('active')) { return; } if (e.key === 'Tab') { const focusableElements = mobileMenu.querySelectorAll( 'a, button, [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; if (e.shiftKey) { // Shift + Tab if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { // Tab if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }); } /** * Initialize focus trap */ setupFocusTrap(); /** * Initialize when DOM is ready */ if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();