The ARIA tree spec says that when the tree widget gets keyboard focus, then the first item should get focus (if no element has focus).
I have implemented this behavior below, but when my click handler fires I get a flash of focus on the first element...
How do I prevent this? (this is in the context of svelte if that matters..)
function ariaTree(node) {
const selectable = node.querySelectorAll('.leaf, summary')
function clickHandler(e) {
selectable.forEach(item => item.classList.remove('selected'))
e.target.classList.add('selected')
}
selectable.forEach(item => {
item.addEventListener('click', clickHandler)
})
// https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
// ..when a .. tree receives focus .. if no node is focused ..
// select the first node.
node.addEventListener('focusin', e => {
if (node.querySelectorAll('.selected').length === 0) {
e.preventDefault() // prevent giving focus to the first "button"-like element
selectable[0].classList.add('selected')
}
})
//return {destroy() {...}}
}
ariaTree(document.querySelector('ul[role="tree"]'))
.wrapper {
display: flex;
&>* {flex: 1}
}
ul[role=tree] li {
list-style-type: none
}
.selected {
background-color: green;
color: white;
}
:where(.leaf, summary):hover {
background-color: navy;
color: white;
}
<div class="wrapper">
<p>
Preceeding element to the tree widget...<br>
Press F7 to enable caret browsing (will display a cursor where the current
keyboard focus is), click inside this text, then tab to get to the
tree widget (the first item should get selection/focus).
<br><br>
Refresh the page (so no item in the tree widget has focus), then click on the last
element (world). There is a flash of focus on the first element...
</p>
<ul role="tree" tabindex="0">
<li class="leaf">
hello
</li>
<li class="branch">
<details open>
<summary>beautiful</summary>
<ul>
<li class="leaf">world</li>
</ul>
</details>
</li>
</ul>
</div>
What happens is:
mousedown
event is triggered,focusin
event is triggered, the 1st element is selectedmouseup
and click
is triggered, and the correct element is highlighted.You need to detect if the focusin
event was caused by a mouse click on an element, or by something else (e.g. tab key).
I think you probably need to add an additional mousedown
event for that purpose. But note that usually you want things to happen on mouseup
, not mousedown
, so keep the click
(or mouseup
) event handler as well.
(That's annoying, I think there should be a better solution, but I can't think of one right now.)