Using tabindex
The default tab order provided by the DOM position of native elements is
convenient, but there are times when you'll want to modify the tab order, and
physically moving elements in the HTML isn't always an optimal, or even a
feasible, solution. For these cases you can use the
tabindex HTML attribute to
explicitly set an element's tab position.
tabindex can be applied to any element - although it is not necessarily
useful on every element - and takes a range of integer values. Using
tabindex, you can specify an explicit order for focusable page elements,
insert an otherwise unfocusable element into the tab order, and remove elements
from the tab order. For example:
tabindex="0": Inserts an element into the natural tab order. The element can
be focused by pressing the
Tab key, and the element can be focused by calling
its
focus() method
Press Tab to Focus Me!
Press Tab to Focus Me!
tabindex="-1": Removes an element from the natural tab order, but the element
can still be focused by calling its
focus() method
tabindex="5": Any tabindex greater than 0 jumps the element to the front of
the natural tab order. If there are multiple elements with a tabindex greater
than 0, the tab order starts from the lowest value that is greater than zero and
works its way up. Using a tabindex greater than 0 is considered an
anti-pattern.
This is particularly true of non-input elements like headers, images, or article
titles. Adding
tabindex to those kinds of elements is counter-productive. If
possible, it's best to arrange your source code so the DOM sequence provides a
logical tab order. If you do use
tabindex, restrict it to custom interactive
controls like buttons, tabs, dropdowns, and text fields; that is, elements the
user might expect to provide input to.
Don't worry about screen reader users missing important content because it
doesn't have a
tabindex. Even if the content is very important, like an image,
if it's not something the user can interact with, there's no reason to make it
focusable. Screen reader users can still understand the content of the image so
long as you provide proper
alt attribute support, which we'll cover shortly.
Managing focus at the page level
Here's a scenario where
tabindex is not only useful, but necessary. You might
be building a robust single page with different content sections, not all of
which are simultaneously visible. In this kind of page, clicking a navigation
link might change the visible content without doing a page refresh.
When this happens, you would probably identify the selected content area, give
it a
tabindex of -1 so that it doesn't appear in the natural tab order, and
call its
focus method. This technique, called managing focus, keeps the
user's perceived context in sync with the site's visual content.
Managing focus in components
Managing focus when you change something on the page is important, but sometimes
you need to manage focus at the control level — for example, if you're
building a custom component.
Consider the native
select element. It can receive basic focus but, once
there, you can use the arrow keys to expose additional functionality (the
selectable options). If you were building a custom
select element, you would
want to expose these same kinds of behaviors so that users who rely primarily on
the keyboard could still interact with your control.
Knowing which keyboard behaviors to implement can be difficult, but there is a
helpful document you can refer to. The
Accessible Rich Internet Applications (ARIA) Authoring
Practices
guide lists types of components and what kinds of keyboard actions they support.
We will cover ARIA in more detail later, but for now let's use the guide to help
us add keyboard support to a new component.
Perhaps you're working on some new Custom
Elements that
resemble a set of radio buttons, but with your unique take on appearance and
behavior.
WaterCoffeeTeaColaGinger Ale
To determine what kind of keyboard support they need, you would check the
ARIA Authoring Practices
guide.
Section 2 contains a list of design patterns, and in that list is a
characteristics table for radio
groups,
the existing component that most closely matches your new element.
As you can see in the table, one of the common keyboard behaviors that should be
supported is the up/down/left/right arrow keys. To add this behavior to the new
component, we'll use a technique called roving tabindex.
W3C spec excerpt for radio buttons.
Roving tabindex works by setting
tabindex to -1 for all children except the
currently-active one.
WaterCoffeeTeaColaGinger Ale
The component then uses a keyboard event listener to determine which key the
user presses; when this happens, it sets the previously focused child's
tabindex to -1, sets the to-be-focused child's
tabindex to 0, and calls the
focus method on it.
// Assuming the user pressed the down arrow, we'll focus the next available child
WaterCoffee // call .focus() on this element
TeaColaGinger Ale
When the user reaches the last (or first, depending on the direction they're
moving the focus) child, you will loop around and focus the first (or last)
child again.
You can give the completed example a try down below. Inspect the element in the
DevTools to observe the tabindex moving from one radio to the next.
Water
Coffee
Tea
Cola
Ginger Ale
You can view
the complete source for this
element
over on GitHub.
Modals and keyboard traps
Sometimes when you're managing focus you can get into a situation you can't get
out of. Consider an autocomplete widget that tries to manage focus and captures
the tab behavior, but prevents the user from leaving it until it's complete.
This is called a keyboard trap, and it can be very frustrating for the user.
Section 2.1.2 of the Web AIM checklist addresses this issue, stating that
keyboard focus should never be locked or trapped at one particular page
element.
The user should be able to navigate to and from all page elements using only the
keyboard.
Oddly, there are times when this behavior is actually desirable, like in a modal
window. Normally, when the modal is displayed, you don't want the user to access
the content behind it. You might add an overlay to cover the page visually, but
that doesn't stop keyboard focus from accidentally traveling outside the modal.
A modal window asking the user to save their work.
In instances like this you can implement a temporary keyboard trap to ensure
that you trap focus only while the modal is displayed and then restore focus to
the previously-focused item when the modal is closed.
There are some proposals on how to make this easier for developers,
including the