mirror of
https://github.com/simonw/datasette.git
synced 2026-07-09 17:14:35 +02:00
- Add aria-haspopup="menu" and aria-expanded to summary element - Add role="menu" to dropdown ul, role="menuitem" and tabindex="-1" to links - Sync aria-expanded via toggle event listener - Focus first menu item when menu opens - Add ArrowDown/ArrowUp navigation between menu items - Add Escape key to close menu and return focus to summary Refs #2738
62 lines
2.3 KiB
HTML
62 lines
2.3 KiB
HTML
<script>
|
|
document.body.addEventListener('click', (ev) => {
|
|
/* Close any open details elements that this click is outside of */
|
|
var target = ev.target;
|
|
var detailsClickedWithin = null;
|
|
while (target && target.tagName != 'DETAILS') {
|
|
target = target.parentNode;
|
|
}
|
|
if (target && target.tagName == 'DETAILS') {
|
|
detailsClickedWithin = target;
|
|
}
|
|
Array.from(document.querySelectorAll('details.details-menu')).filter(
|
|
(details) => details.open && details != detailsClickedWithin
|
|
).forEach(details => details.open = false);
|
|
});
|
|
|
|
/* Sync aria-expanded and add keyboard navigation for details-menu elements */
|
|
document.querySelectorAll('details.details-menu').forEach(function(details) {
|
|
var summary = details.querySelector('summary');
|
|
details.addEventListener('toggle', function() {
|
|
if (summary) {
|
|
summary.setAttribute('aria-expanded', details.open ? 'true' : 'false');
|
|
}
|
|
if (details.open) {
|
|
/* Focus first menu item when menu opens */
|
|
var firstItem = details.querySelector('[role="menuitem"]');
|
|
if (firstItem) { firstItem.focus(); }
|
|
}
|
|
});
|
|
});
|
|
|
|
document.body.addEventListener('keydown', function(ev) {
|
|
/* Keyboard navigation for open details-menu elements */
|
|
var openDetails = Array.from(document.querySelectorAll('details.details-menu[open]'));
|
|
if (!openDetails.length) { return; }
|
|
|
|
if (ev.key === 'Escape') {
|
|
openDetails.forEach(function(details) {
|
|
details.open = false;
|
|
var summary = details.querySelector('summary');
|
|
if (summary) { summary.focus(); }
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') {
|
|
var focused = document.activeElement;
|
|
openDetails.forEach(function(details) {
|
|
var items = Array.from(details.querySelectorAll('[role="menuitem"]'));
|
|
if (!items.length) { return; }
|
|
var idx = items.indexOf(focused);
|
|
if (idx === -1) { return; }
|
|
ev.preventDefault();
|
|
if (ev.key === 'ArrowDown') {
|
|
items[(idx + 1) % items.length].focus();
|
|
} else {
|
|
items[(idx - 1 + items.length) % items.length].focus();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|