Last active
February 8, 2022 08:31
-
-
Save AlejandroPerezMartin/f4af580af000efa92f2d767931a2f893 to your computer and use it in GitHub Desktop.
Accessible dropdown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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