Skip to content

Instantly share code, notes, and snippets.

@AlejandroPerezMartin
Last active February 8, 2022 08:31
Show Gist options
  • Save AlejandroPerezMartin/f4af580af000efa92f2d767931a2f893 to your computer and use it in GitHub Desktop.
Save AlejandroPerezMartin/f4af580af000efa92f2d767931a2f893 to your computer and use it in GitHub Desktop.
Accessible dropdown
/**
* Dropdown Component
*
* Add 'data-dropdown' and 'aria-controls' with the element id to toggle
*/
const Dropdown = () => {
const dropdowns = document.querySelectorAll('[data-dropdown]');
// If there's no wrapper, bail.
if (!dropdowns) {
return;
}
const initDropdown = (dropdown) => {
const targetId = dropdown.getAttribute('aria-controls');
const expandedElement = document.getElementById(targetId);
if (!expandedElement) {
return;
}
/**
* Toggle dropdown visibility
*
* @param {boolean} expand Set expand status
*/
const toggleDropdown = (expand) => {
dropdown.setAttribute('aria-expanded', expand);
expandedElement.setAttribute('aria-hidden', !expand);
};
/**
* Close dropdown on outside click
*
* @param {Event} event Click event
*/
const globalOnClick = (event) => {
const { target } = event;
const isClickInside = expandedElement.contains(target);
if (
(!dropdown.contains(target) && !isClickInside) ||
(isClickInside && target.tagName.toLowerCase() === 'a')
) {
toggleDropdown(false);
// eslint-disable-next-line no-use-before-define
removeEvents();
}
};
/**
* Close dropdown if Escape key is pressed
*
* @param {Event} event Keyup event
*/
const onKeyup = (event) => {
// 27: Escape key
if (event.keyCode === 27) {
toggleDropdown(false);
// eslint-disable-next-line no-use-before-define
removeEvents();
}
};
/**
* Remove global event listeners
*/
const removeEvents = () => {
// eslint-disable-next-line @wordpress/no-global-event-listener
document.removeEventListener('click', globalOnClick);
// eslint-disable-next-line @wordpress/no-global-event-listener
document.removeEventListener('keyup', onKeyup);
};
/**
* Dropdown toggle click handler
*
* @param {Event} event Click event
*/
const onToggleClick = (event) => {
const isExpanded = dropdown.getAttribute('aria-expanded') === 'true';
toggleDropdown(!isExpanded);
if (!isExpanded) {
// Avoid triggering events declared below
event.stopPropagation();
// eslint-disable-next-line @wordpress/no-global-event-listener
document.addEventListener('click', globalOnClick);
// eslint-disable-next-line @wordpress/no-global-event-listener
document.addEventListener('keyup', onKeyup);
}
};
dropdown.addEventListener('click', onToggleClick);
};
dropdowns.forEach((dropdown) => initDropdown(dropdown));
};
export default Dropdown;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment