[Proposal] Focus Traversal API


#1

Focus Traversal API

The current system for programmatically manipulating focus within a web page leaves a lot to be desired. A single element can request the focus with the focus() method but that is the extent of the programatic focus navigation options. Advancing the focus to the next focusable element or the previous focusable element involves a complex dance of DOM traversal and guess work as to what elements can recieve focus or not. Most solutions to manipulating focus are complicated; the very simple need to manipulate focus in a meaningful and accessalbe manner is significantly lacking.

Proposed

The creation of a unified Focus Traversal API that makes understanding, manipulating, and traversing the focus simple. This must include the ability to assign the focus, move the focus forward and backward, and understand both the next and previous focusable elements.

A rough example of this API might be the following:

window.focusManager.currentlyFocused - Contains the element currently holding the focus, if any.

window.focusManager.previouslyFocused - Contains the element that held the focus prior to the current focus, if any.

window.focusManager.history - An array of the last n historical focus holders.

window.focusManager.hasFocus(element) - Returns true if the given element currently has the focus. Functionally equivelent to window.focusManager.currentlyFocused===element.

window.focusManager.focus(element) - Focus on the given element. Functionally the same as element.focus(). Returns void.

window.focusManager.forward() - Move the focus to the next focusable element. Returns void.

window.focusManager.backward() - Move the focus to the previous focusable element. Returns void.

window.focusManager.next(element) - Returns the element that would revieve the focus if window.focusManager.forward() was called when the given element has the focus. If no element is given, the currently focused element is used.

window.focusManager.previous(element) - Returns the element that would revieve the focus if window.focusManager.backward() was called when the given element has the focus. If no element is given, the currently focused element is used.

window.focusManager.orderedElements() - Returns an array of all focusable elements in the order that focus traversal would occur.

Illustrative User Issues

Example Polyfill

An example implementation of the above as well as this document can be found at https://github.com/awesomeeng/FocusTraversalAPI.

Similar Works

https://github.com/davidtheclark/tabbable - A very popular library for getting a list of all the elements in a element that can recieve the focus.


#2

It’s great idea, I had this issue myself.


#3

Updated Polyfill to v1.1.0 with support for Shadow DOM traversal.


#4

If a new API is being proposed, why is it synchronous? Is there a technical issue if these return Promises that resolve to their values?


#5

Why would it need to be Asynchronous? None of what it does happens asynchronously. See the Polyfill for examples.


#6

I’m just curious as to whether it would have any known implications. I get the feeling going forward web standards are trying to make anything new as asynchronous as possible. That way we can do as much work as possible without blocking the main thread. Simply because the APIs currently available are all synchronous doesn’t mean investigating new ones being async isn’t an option.


#7

I currently don’t foresee any type of asynchronous need around focus traversal. focus has always been a synchronous process and I am not proposing changing that in any manner. Focus Traversal as proposed is an addition to existing focus systems, a quality of life improvement without any major impact on the existing focus system. If anyone can foresee any asynchronous need, please do share.

I am also purposely not proposing the addition of any of the higher level ideas around focus such as trapping or focus groups.These are great ideas and I would love to see them, but keeping the proposal simple, clean and very implementable was my first priority.

Glen


#8

The problem is something like orderedElements. This could end up returning a large set of nodes. While the array is being compiled this could block the main thread. So at least this item should be async. Sure a few hundred nodes shouldn’t be a huge issue. But in poorly built systems they could have/end up with multiple thousands on a page.

At least in that case, async makes sense. Which is why I’m bringing up async investigation for the whole API if it moves forwards. Could it be done? What would the drawbacks be in engines to implement that?

I love the idea and I’d personally like to see it brought into browsers. I just want to see some investigation into async if it moves forward so the path doesn’t go untravelled.


#9

I can see what you are saying there. The question is do we drop orderedElements entirely or make it an async function? Is there a need for it?