blob: 1b9a39132d6f10a24ac5f813b687b0ab17fde201 [file] [log] [blame]
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
export function registerHeaderListeners(): void {
const header = document.querySelector('.js-header') as HTMLElement;
// Desktop menu hover state
const menuItemHovers = document.querySelectorAll('.js-desktop-menu-hover');
menuItemHovers.forEach(menuItemHover => {
// when user clicks on the dropdown menu item on desktop or mobile,
// force the menu to stay open until the user clicks off of it.
menuItemHover.addEventListener('mouseenter', e => {
const target = e.target as HTMLElement;
const forced = document.querySelector('.forced-open') as HTMLElement;
if (forced && forced !== menuItemHover) {
forced.blur();
forced.classList.remove('forced-open');
}
// prevents menus that have been tabbed into from staying open
// when you hover over another menu
target.classList.remove('forced-closed');
target.classList.add('forced-open');
});
const toggleForcedOpen = (e: Event) => {
const target = e.target as HTMLElement;
const isForced = target?.classList.contains('forced-open');
const currentTarget = e.currentTarget as HTMLElement;
if (isForced) {
currentTarget.removeEventListener('blur', () =>
currentTarget.classList.remove('forced-open')
);
currentTarget.classList.remove('forced-open');
currentTarget.classList.add('forced-closed');
currentTarget.blur();
currentTarget?.parentNode?.addEventListener('mouseout', () => {
currentTarget.classList.remove('forced-closed');
});
} else {
currentTarget.classList.remove('forced-closed');
currentTarget.classList.add('forced-open');
currentTarget.focus();
currentTarget.addEventListener('blur', () => currentTarget.classList.remove('forced-open'));
currentTarget?.parentNode?.removeEventListener('mouseout', () => {
currentTarget.classList.remove('forced-closed');
});
}
currentTarget.focus();
};
menuItemHover.addEventListener('click', toggleForcedOpen);
menuItemHover.addEventListener('focus', e => {
const target = e.target as HTMLElement;
target.classList.add('forced-closed');
target.classList.remove('forced-open');
});
// ensure desktop submenus are closed when esc is pressed
const closeSubmenuOnEsc = (e: Event) => {
const event = e as KeyboardEvent;
const target = e.target as HTMLElement;
if (event.key === 'Escape') {
const forcedOpenItem = document.querySelector('.forced-open') as HTMLElement;
if (forcedOpenItem) {
forcedOpenItem.classList.remove('forced-open');
forcedOpenItem.blur();
forcedOpenItem.classList.add('forced-closed');
target?.focus();
}
}
};
document.addEventListener('keydown', closeSubmenuOnEsc);
});
// Mobile menu subnav menus
const headerbuttons = document.querySelectorAll('.js-headerMenuButton');
headerbuttons.forEach(button => {
button.addEventListener('click', e => {
e.preventDefault();
const isActive = header?.classList.contains('is-active');
if (isActive) {
handleNavigationDrawerInactive(header);
} else {
handleNavigationDrawerActive(header);
}
button.setAttribute('aria-expanded', isActive ? 'true' : 'false');
});
});
const scrim = document.querySelector('.js-scrim');
scrim?.addEventListener('click', e => {
e.preventDefault();
// find any active submenus and close them
const activeSubnavs = document.querySelectorAll('.go-NavigationDrawer-submenuItem.is-active');
activeSubnavs.forEach(subnav => handleNavigationDrawerInactive(subnav as HTMLElement));
handleNavigationDrawerInactive(header);
headerbuttons.forEach(button => {
button.setAttribute(
'aria-expanded',
header?.classList.contains('is-active') ? 'true' : 'false'
);
});
});
const getNavigationDrawerMenuItems = (navigationDrawer: HTMLElement): HTMLElement[] => {
if (!navigationDrawer) {
return [];
}
const menuItems = Array.from(
navigationDrawer.querySelectorAll(
':scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a'
) || []
);
const anchorEl = navigationDrawer.querySelector('.go-NavigationDrawer-header > a');
if (anchorEl) {
menuItems.unshift(anchorEl);
}
return menuItems as HTMLElement[];
};
const getNavigationDrawerIsSubnav = (navigationDrawer: HTMLElement) => {
if (!navigationDrawer) {
return;
}
return navigationDrawer.classList.contains('go-NavigationDrawer-submenuItem');
};
const handleNavigationDrawerInactive = (navigationDrawer: HTMLElement) => {
if (!navigationDrawer) {
return;
}
const menuItems = getNavigationDrawerMenuItems(navigationDrawer);
navigationDrawer.classList.remove('is-active');
const parentMenuItem = navigationDrawer
.closest('.go-NavigationDrawer-listItem')
?.querySelector(':scope > a') as HTMLElement;
parentMenuItem?.focus();
menuItems?.forEach(item => item?.setAttribute('tabindex', '-1'));
if (menuItems && menuItems[0]) {
menuItems[0].removeEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));
menuItems[menuItems.length - 1].removeEventListener(
'keydown',
handleMenuItemTabRightFactory(navigationDrawer)
);
}
if (navigationDrawer === header) {
headerbuttons && (headerbuttons[0] as HTMLElement)?.focus();
}
};
const handleNavigationDrawerActive = (navigationDrawer: HTMLElement) => {
const menuItems = getNavigationDrawerMenuItems(navigationDrawer);
navigationDrawer.classList.add('is-active');
menuItems.forEach(item => item.setAttribute('tabindex', '0'));
menuItems[0].focus();
menuItems[0].addEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));
menuItems[menuItems.length - 1].addEventListener(
'keydown',
handleMenuItemTabRightFactory(navigationDrawer)
);
};
const handleMenuItemTabLeftFactory = (navigationDrawer: HTMLElement) => {
return (e: KeyboardEvent) => {
if (e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
handleNavigationDrawerInactive(navigationDrawer);
}
};
};
const handleMenuItemTabRightFactory = (navigationDrawer: HTMLElement) => {
return (e: KeyboardEvent) => {
if (e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
handleNavigationDrawerInactive(navigationDrawer);
}
};
};
const prepMobileNavigationDrawer = (navigationDrawer: HTMLElement) => {
const isSubnav = getNavigationDrawerIsSubnav(navigationDrawer);
const menuItems = getNavigationDrawerMenuItems(navigationDrawer);
navigationDrawer.addEventListener('keyup', e => {
if (e.key === 'Escape') {
handleNavigationDrawerInactive(navigationDrawer);
}
});
menuItems.forEach(item => {
const parentLi = item.closest('li');
if (parentLi && parentLi.classList.contains('js-mobile-subnav-trigger')) {
const submenu = parentLi.querySelector('.go-NavigationDrawer-submenuItem') as HTMLElement;
item.addEventListener('click', () => {
handleNavigationDrawerActive(submenu);
});
}
});
if (isSubnav) {
handleNavigationDrawerInactive(navigationDrawer);
navigationDrawer
?.querySelector('.go-NavigationDrawer-header')
?.addEventListener('click', e => {
e.preventDefault();
handleNavigationDrawerInactive(navigationDrawer);
});
}
};
document
.querySelectorAll('.go-NavigationDrawer')
.forEach(drawer => prepMobileNavigationDrawer(drawer as HTMLElement));
handleNavigationDrawerInactive(header);
}
export function registerSearchFormListeners(): void {
const searchForm = document.querySelector('.js-searchForm');
const expandSearch = document.querySelector('.js-expandSearch');
const input = searchForm?.querySelector('input');
const headerLogo = document.querySelector('.js-headerLogo');
const menuButton = document.querySelector('.js-headerMenuButton');
expandSearch?.addEventListener('click', () => {
searchForm?.classList.add('go-SearchForm--expanded');
headerLogo?.classList.add('go-Header-logo--hidden');
menuButton?.classList.add('go-Header-navOpen--hidden');
input?.focus();
});
document?.addEventListener('click', e => {
if (!searchForm?.contains(e.target as Node)) {
searchForm?.classList.remove('go-SearchForm--expanded');
headerLogo?.classList.remove('go-Header-logo--hidden');
menuButton?.classList.remove('go-Header-navOpen--hidden');
}
});
}