trihub-ernaehrung-advinters/js/navigation.js

77 lines
3.0 KiB
JavaScript

(() => {
const navigation = document.querySelector('.site-navigation');
if (!navigation) return;
const button = navigation.querySelector('.menu-toggle');
const links = navigation.querySelector('#navigation-links');
const backdrop = navigation.querySelector('.site-navigation__backdrop');
if (!button || !links || !backdrop) return;
// Muss zum Grenzwert in navigation.css passen.
const mobileView = window.matchMedia('(max-width: 1100px)');
function setMenuOpen(isOpen) {
isOpen = isOpen && mobileView.matches;
navigation.classList.toggle('is-open', isOpen);
button.setAttribute('aria-expanded', String(isOpen));
button.setAttribute('aria-label', isOpen ? 'Menü schließen' : 'Menü öffnen');
button.textContent = isOpen ? '✕' : '☰';
}
button.addEventListener('click', () => {
setMenuOpen(button.getAttribute('aria-expanded') !== 'true');
});
// Nach einem Sprung bleibt der Tastaturfokus im gewählten Bereich.
links.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (!link || !mobileView.matches) return;
setMenuOpen(false);
// Nur Anker-Sprünge innerhalb der Seite; Links zu anderen Seiten übernimmt der Browser.
const href = link.getAttribute('href');
if (!href || !href.startsWith('#')) return;
const target = document.querySelector(href);
if (target) {
const hadTabindex = target.hasAttribute('tabindex');
if (!hadTabindex) target.setAttribute('tabindex', '-1');
requestAnimationFrame(() => target.focus({ preventScroll: true }));
if (!hadTabindex) {
target.addEventListener('blur', () => target.removeAttribute('tabindex'), { once: true });
}
}
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && button.getAttribute('aria-expanded') === 'true') {
setMenuOpen(false);
button.focus();
}
});
backdrop.addEventListener('click', () => {
setMenuOpen(false);
button.focus();
});
// Das Menü bleibt beim Weitergehen mit Tab nicht über dem Seiteninhalt liegen.
navigation.addEventListener('focusout', (event) => {
if (!navigation.contains(event.relatedTarget)) setMenuOpen(false);
});
mobileView.addEventListener('change', () => {
const focusedElement = document.activeElement;
setMenuOpen(false);
if (mobileView.matches && links.contains(focusedElement)) button.focus();
if (!mobileView.matches && focusedElement === button) links.querySelector('a')?.focus();
});
// Berücksichtigt auch umgebrochene Texte und vergrößerte Schrift.
const headerObserver = new ResizeObserver(() => {
document.documentElement.style.setProperty(
'--navigation-offset', `${navigation.getBoundingClientRect().height}px`
);
});
headerObserver.observe(navigation);
setMenuOpen(false);
})();