From da627d398c0f4b3bb7569dc85b92d1b93a5e97cc Mon Sep 17 00:00:00 2001 From: Francesc Arpi Roca Date: Tue, 31 Mar 2026 13:34:31 +0200 Subject: [PATCH] fix: improve navigation active state detection with hash support - Compare full URLs (pathname + hash) instead of just pathname - Add event listeners for hashchange, popstate, and menu clicks - Fixes issue where multiple menu items with same path but different hashes were all marked active - Ensures active state updates immediately when clicking hash-based navigation links --- .../Header/components/Navigation.astro | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/layouts/components/Header/components/Navigation.astro b/src/layouts/components/Header/components/Navigation.astro index 73159e2..ef8ea6e 100644 --- a/src/layouts/components/Header/components/Navigation.astro +++ b/src/layouts/components/Header/components/Navigation.astro @@ -222,6 +222,10 @@ const { items } = menuTexts[currentLang] const setActiveMenuItem = (): void => { const allMenus = mainNav.querySelectorAll('nav > ul') const currentPathname = window.location.pathname + const currentHash = window.location.hash + + // Build the full current URL (without trailing slash in pathname) + const currentFullUrl = currentPathname.replace(/\/$/, '') + currentHash allMenus.forEach((menu) => { const menuItems = [...menu.querySelectorAll('a:not([rel*="external"])')] as HTMLAnchorElement[] @@ -231,10 +235,17 @@ const { items } = menuTexts[currentLang] menuItem.classList.remove('is-active') menuItem.removeAttribute('aria-current') - const itemPathname = menuItem.pathname.replace(/\/$/, '') // Remove trailing slash - const currentPath = currentPathname.replace(/\/$/, '') - - if (itemPathname === currentPath || (itemPathname === '' && currentPath === '')) { + // Build the full URL for the menu item + const itemPathname = menuItem.pathname.replace(/\/$/, '') + const itemHash = menuItem.hash || '' + const itemFullUrl = itemPathname + itemHash + + // Compare full URLs + if ( + itemFullUrl === currentFullUrl || + (itemFullUrl === '' && currentFullUrl === '') || + (itemPathname === '' && currentPathname.replace(/\/$/, '') === '') + ) { menuItem.classList.add('is-active') menuItem.setAttribute('aria-current', 'page') } @@ -474,6 +485,27 @@ const { items } = menuTexts[currentLang] // Initialize active menu item setActiveMenuItem() + + // Update active menu item when hash changes (for same-page navigation) + window.addEventListener('hashchange', setActiveMenuItem) + + // Also listen for popstate (back/forward navigation) + window.addEventListener('popstate', setActiveMenuItem) + + // Listen for clicks on menu items to update immediately + const allMenus = mainNav.querySelectorAll('nav > ul') + allMenus.forEach((menu) => { + menu.addEventListener('click', (event) => { + const target = event.target as HTMLElement + const link = target.closest('a') as HTMLAnchorElement + if (link && !link.hasAttribute('rel')) { + // Small delay to ensure URL has changed + setTimeout(() => { + setActiveMenuItem() + }, 10) + } + }) + }) })