dequelabs / dequelabs/axe-core
False positive: scrollable-region-focusable on menus with programmatic keyboard focus
- Dominant language
- JavaScript
- Stars
- 7.5k
- Forks
- 933
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 17
Description
### Product
axe-core
### Product Version
4.13.0 (develop)
### Latest Version
- [x] I have tested the issue with the latest version of the product
### Issue Description
#### Expectation
`scrollable-region-focusable` should pass. The list is keyboard accessible: activating the Products button (or ArrowDown on it) moves focus to the first item, and arrow keys move between items.
#### Actual
axe reports a violation on `#menu` (`Element should have focusable content` / `Element should be focusable`).
#### How to Reproduce
```html
#menu {
max-height: 6em;
overflow-y: auto;
width: 12em;
margin: 0;
padding: 0;
border: 1px solid;
list-style: none;
}
#menu button {
display: block;
width: 100%;
text-align: left;
}
Products
- Keyboards
- Mice
- Monitors
- Headsets
- Webcams
- Microphones
- Docking stations
- Cables
const menubutton = document.getElementById('menubutton');
const menu = document.getElementById('menu');
const menuitems = [...menu.querySelectorAll('button')];
function setExpanded(isExpanded) {
menubutton.setAttribute('aria-expanded', String(isExpanded));
menu.hidden = !isExpanded;
}
menubutton.addEventListener('click', () => {
const isExpanded = menubutton.getAttribute('aria-expanded') === 'true';
setExpanded(!isExpanded);
if (!isExpanded) {
menuitems[0].focus();
}
});
menubutton.addEventListener('keydown', event => {
if (event.key !== 'ArrowDown') {
return;
}
event.preventDefault();
setExpanded(true);
menuitems[0].focus();
});
menu.addEventListener('keydown', event => {
const index = menuitems.indexOf(event.target);
const offset = { ArrowDown: 1, ArrowUp: -1 }[event.key];
if (index === -1 || !offset) {
return;
}
event.preventDefault();
menuitems[(index + offset + menuitems.length) % menuitems.length].focus();
});
```
#### Additional context
Customer mega menu. Items stay `tabindex="-1"` and are focused by script. `tabindex="-1"` on the `ul` is only there to stop Chrome from focusing the scroll container.
Contributor guide
Research direction
Start by reproducing the supplied menu example and inspect the scrollable-region-focusable rule, focusing on how programmatically focused menu items and the tabindex="-1" scroll container are evaluated. Add a regression test for this markup, then verify the menu no longer reports a violation while existing scrollable-region checks still behave as expected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- accessibility, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100