/* * CTAO portal mockup: header behaviour * * 1. The sticky header turns solid Galaxy Blue after 40px of scroll. * 2. Submenus: every button with data-submenu-toggle (Example, language) * toggles the element named in its aria-controls. One open at a time. * 3. The search button toggles the search panel. * 4. The burger button toggles the main nav on small screens. * 5. Escape and clicks outside the header close open menus. * * Review helper: index.html#open= opens a panel on load, * for example #open=submenu-example, #open=submenu-lang, * #open=search-panel or #open=site-nav. */ (function () { "use strict"; // Keep in sync with the small-screen media query in css/styles.css const MOBILE_QUERY = window.matchMedia("(max-width: 1079px)"); const header = document.getElementById("site-header"); const nav = document.getElementById("site-nav"); const searchPanel = document.getElementById("search-panel"); const searchButton = header.querySelector('[aria-controls="search-panel"]'); const menuButton = header.querySelector('[aria-controls="site-nav"]'); const submenuButtons = Array.from( header.querySelectorAll("[data-submenu-toggle]"), ); /* 1. Scrolled state */ function updateScrolled() { header.classList.toggle("is-scrolled", window.scrollY > 40); } window.addEventListener("scroll", updateScrolled, { passive: true }); updateScrolled(); /* Keeps the header solid while a full-width panel is open */ function updateHeaderState() { const panelOpen = !searchPanel.hidden || header.classList.contains("is-nav-open"); header.classList.toggle("is-open", panelOpen); } /* 2. Submenus */ function setSubmenu(button, open) { const submenu = document.getElementById( button.getAttribute("aria-controls"), ); button.setAttribute("aria-expanded", String(open)); submenu.hidden = !open; } function closeSubmenus(except) { submenuButtons.forEach(function (button) { if (button !== except) setSubmenu(button, false); }); } submenuButtons.forEach(function (button) { button.addEventListener("click", function () { const open = button.getAttribute("aria-expanded") !== "true"; closeSubmenus(button); setSearch(false); setSubmenu(button, open); }); }); /* 3. Search */ function setSearch(open) { searchButton.setAttribute("aria-expanded", String(open)); searchPanel.hidden = !open; updateHeaderState(); if (open) searchPanel.querySelector("input").focus(); } searchButton.addEventListener("click", function () { const open = searchPanel.hidden; closeSubmenus(); setMobileNav(false); setSearch(open); }); /* 4. Mobile nav */ function setMobileNav(open) { menuButton.setAttribute("aria-expanded", String(open)); header.classList.toggle("is-nav-open", open); document.body.classList.toggle("no-scroll", open); updateHeaderState(); } menuButton.addEventListener("click", function () { const open = !header.classList.contains("is-nav-open"); closeSubmenus(); setSearch(false); setMobileNav(open); }); /* 5. Dismiss */ function closeAll() { closeSubmenus(); setSearch(false); setMobileNav(false); } document.addEventListener("click", function (event) { if (!header.contains(event.target)) closeAll(); }); document.addEventListener("keydown", function (event) { if (event.key !== "Escape") return; const openSubmenuButton = submenuButtons.find(function (button) { return button.getAttribute("aria-expanded") === "true"; }); if (openSubmenuButton) { setSubmenu(openSubmenuButton, false); openSubmenuButton.focus(); } else if (!searchPanel.hidden) { setSearch(false); searchButton.focus(); } else if (header.classList.contains("is-nav-open")) { setMobileNav(false); menuButton.focus(); } }); /* Switching between desktop and small-screen layouts starts clean */ MOBILE_QUERY.addEventListener("change", closeAll); /* Review helpers */ const openMatch = location.hash.match(/^#open=([\w-]+)$/); if (openMatch) { const target = header.querySelector( '[aria-controls="' + openMatch[1] + '"]', ); if (target) { if (MOBILE_QUERY.matches && nav.contains(target)) menuButton.click(); target.click(); } } if (location.hash === "#scrolled") window.scrollTo(0, 420); })();