I’ve never implemented this, but I’ve looked into a similar problem, and here’s what I would try.
Referencing the jQuery implementation, you must:
- Listen for Tab and Shift+Tab
- Know which elements are tab-able
- Understand how tab order works
1. Listen for Tab and Shift+Tab
Listening for Tab and Shift+Tab are probably well-covered elsewhere on the web, so I’ll skip that part.
2. Know which elements are tab-able
Knowing which elements are tab-able is trickier. Basically, an element is tab-able if it is focusable and does not have the attribute tabindex="-1"
set. So then we must ask which elements are focusable. The following elements are focusable:
input
,select
,textarea
,button
, andobject
elements that aren’t disabled.a
andarea
elements that have anhref
or have a numerical value fortabindex
set.- any element that has a numerical value for
tabindex
set.
Furthermore, an element is focusable only if:
- None of its ancestors are
display: none
. - The computed value of
visibility
isvisible
. This means that the nearest ancestor to havevisibility
set must have a value ofvisible
. If no ancestor hasvisibility
set, then the computed value isvisible
.
More details are in another Stack Overflow answer.
3. Understand how tab order works
The tab order of elements in a document is controlled by the tabindex
attribute. If no value is set, the tabindex
is effectively 0
.
The tabindex
order for the document is: 1, 2, 3, …, 0.
Initially, when the body
element (or no element) has focus, the first element in the tab order is the lowest non-zero tabindex
. If multiple elements have the same tabindex
, you then go in document order until you reach the last element with that tabindex
. Then you move to the next lowest tabindex
and the process continues. Finally, finish with those elements with a zero (or empty) tabindex
.