class Navigation {
|
constructor() {
|
this.counter = 0;
|
this.initElements();
|
if (this.navs.length === 0) {
|
return;
|
}
|
|
this.openNav = null;
|
this.initListeners();
|
}
|
|
initElements() {
|
this.navs = new Map();
|
document.querySelectorAll('nav:has(.submenu), nav:has(.toggle)').forEach(nav => {
|
let navID = nav.id;
|
if (navID === '') {
|
navID = `nav-${this.counter}`;
|
nav.id = navID;
|
this.counter++;
|
}
|
if (nav.querySelector('.submenu')) {
|
nav.addEventListener('mouseenter', this.hoverOnListener);
|
nav.addEventListener('mouseleave', this.hoverOffListener);
|
}
|
|
let [
|
toggles,
|
submenus,
|
submenuToggles
|
] = [
|
nav.querySelectorAll('nav .toggle'),
|
nav.querySelectorAll('.has-submenu'),
|
nav.querySelectorAll('.toggle:not(.main)')
|
];
|
let elements = {
|
nav: nav,
|
toggles: toggles,
|
submenus: submenus,
|
submenuToggles: submenuToggles
|
}
|
this.navs.set(navID, elements);
|
this.counter++;
|
});
|
}
|
|
navIDs() {
|
return Array.from(this.navs.keys()).map((nav) => `#${nav}`);
|
}
|
|
initListeners() {
|
this.clickListener = this.handleClick.bind(this);
|
this.escapeListener = this.handleEscape.bind(this);
|
this.hoverOnListener = this.handleHoverOn.bind(this);
|
this.hoverOffListener = this.handleHoverOff.bind(this);
|
|
document.addEventListener('click', this.clickListener);
|
}
|
handleClick(e) {
|
if (this.openNav && !e.target.closest(this.openNav)) {
|
this.toggleNav(false);
|
}
|
if (!e.target.closest(... this.navIDs())) {
|
return;
|
}
|
|
let toggle = e.target.closest('.toggle.main');
|
if (toggle) {
|
let nav = toggle.closest('nav');
|
this.toggleNav(!nav.classList.contains('open'), nav.id);
|
}
|
}
|
|
handleHoverOn(e) {
|
console.log(e.target);
|
let nav = e.target.closest('nav');
|
if (nav) {
|
this.toggleNav(true, nav.id);
|
}
|
let submenu = e.target.closest('.has-submenu');
|
if (submenu) {
|
this.toggleSubmenu(true, submenu);
|
}
|
}
|
|
handleHoverOff(e) {
|
console.log(e.target);
|
let nav = e.target.closest('nav');
|
if (nav) {
|
this.toggleNav(false, nav.id);
|
}
|
// let submenu = e.target.closest('.has-submenu');
|
// if (submenu) {
|
// this.toggleSubmenu(false, submenu);
|
// }
|
}
|
|
handleEscape(e) {
|
if (this.openNav && e.key === 'Escape') {
|
this.toggleNav(false, this.openNav);
|
}
|
}
|
|
toggleNav(on, id) {
|
let nav = this.navs.get(id);
|
if (!nav) {
|
return;
|
}
|
|
if (on && id !== this.openNav) {
|
this.toggleNav(false, this.openNav);
|
}
|
if (on) {
|
this.openNav = id;
|
document.addEventListener('keydown', this.escapeListener);
|
} else {
|
if (this.openNav === id) {
|
this.openNav = null;
|
}
|
document.removeEventListener('keydown', this.escapeListener);
|
Array.from(nav.submenus).forEach(submenu => {
|
if(submenu.classList.contains('open')) {
|
this.toggleSubmenu(false, submenu);
|
}
|
});
|
}
|
|
nav.nav.ariaExpanded = on;
|
nav.nav.classList.toggle('open', on);
|
nav.ariaHidden = !on;
|
if (on) {
|
nav.nav.querySelector('a:not(.skip-to-content)')?.focus();
|
}
|
}
|
|
toggleSubmenu(on, submenu) {
|
let [
|
toggle,
|
firstLink
|
] = [
|
submenu.querySelector('.toggle'),
|
submenu.querySelector('a')
|
];
|
|
submenu.classList.toggle('open', on);
|
submenu.ariaHidden = !on;
|
toggle.ariaExpanded = on;
|
if (on && firstLink) {
|
firstLink.focus();
|
}
|
}
|
|
}
|
|
document.addEventListener('DOMContentLoaded', function() {
|
window.jvbNav = new Navigation();
|
});
|