Check out a free preview of the full Web App Accessibility (feat. React) course:
The "Tab Key Navigation & Active Element" Lesson is part of the full, Web App Accessibility (feat. React) course featured in this preview video. Here's what you'd learn in this lesson:

Marcy explains some best practices for tab-based navigation and talks through tab key versus arrow key approaches. The Active Element API is also introduced. It provides easy access to the currently focused element.

Get Unlimited Access Now

Transcript from the "Tab Key Navigation & Active Element" Lesson

>> Tab key navigation, another pattern. So a question I get a lot is when to use the arrow keys when you're creating accessible user interfaces. So we use the Tab key for links, buttons, form controls, etc. I think there's a temptation, and I guess we skipped down the page.

[00:00:22] There is a temptation to use the arrow keys for things like menus. And on websites, it's usually not necessary. So there are your menu rules, that is an ARIA rule that you can use. There's a lot of components that have to go into it, including wiring up Javascript to work on arrow keys.

[00:00:47] We tend to only recommend that ARIA menu for really app-like kind of a desktop style app menu. So if you're just writing a header for a website with lists of links, you don't need all of that. You can just keep it simple, let things navigate in their normal tab order with the Tab key.

[00:01:07] Just use lists of links, let them have their normal roles. Don't even bother with ARIA role menu, cuz often people sap ARIA role menu and then they don't do the rest of the pattern, and it's a lot of work. So yeah, it's another ARIA pitfall. But areas where we do want Tab key navigation, we can make it happen using tabindex.

[00:01:29] So we learned about tabindex so far, it takes different values. So tabindex of 0 will put something in the tab order when it wasn't already. If it has positive values like 1, 5, 32,000, [LAUGH] I've seen that before, that will put it in an ordered tab order. And it's tough, cuz you have to manage all of those numbers.

[00:01:55] So just use 0, everything will go in the order that it is in the DOM. That -1 will pull a link or a button out of the tab order that was, otherwise, reachable. Or it'll add the ability to script focus like we saw with the wrapper element technique on the skip links.

[00:02:13] So tabindex is how we make Tab key navigation work, and I'll show you what I mean. If I scroll down here to an example, we have this little tabs switcher. So I hit Tab, only one of these tabs is actually reachable with the Tab key. Instead, if I use the arrow keys on these tabs, it can cycle through.

[00:02:41] And so that's what we mean when we say, Tab key navigation. And so this example, this is an example from the ARIA Authoring Practices Guide. So it's really down to basics showing you with HTML, CSS, and JavaScript how to make an accessible tab switcher. But like I was saying for lists of links, it would be overkill, or we just don't need to implement something that robust for menus.

[00:03:11] Usually, if it's a web app, maybe it would be more appropriate. So to make this happen, let's make this. I guess Stack Blitz won't get any bigger. So for their implementation, we have the ARIA roles for a tab list. These buttons are reachable by default, so they're focusable just like any button until you put tabindex of -1.

[00:03:41] So this uses a technique that's called roving tabindex, where only one button in a set has tabindex of 0. And every other one you set to the -1, so that's how we could see. Let's do this. So each one of these, before you load the JavaScript, these are all reachable.

[00:04:04] But JavaScript goes in, I guess it depends on how they wrote this. But the tabindex of -1 on each one of these will mean that you have to use JavaScript to move focus around, so it's dynamic. Other patterns that this would be useful for are date pickers. If you have a calendar, you don't wanna tab through 30 days.

[00:04:26] [LAUGH] That'd be so annoying. So we'd use this technique for when we want one tab stop for a calendar, for example, and the arrow keys will navigate you through all these items. So the tabindex is really the workhorse here of making this work and JavaScript and probably some CSS.

[00:04:45] But what I'm saying is that tabindex is really powerful, and so we can make these interactive widgets with some scripting. And so I like these ARIA Authoring Practices Guide examples, cuz they do sort of strip away some of the other things that we see in web apps, like Typescript, and state, and all those things.

[00:05:08] So the alternative to some of that code, and let's go over here and see what their script looks like, cuz this uses just vanilla JavaScript. So they iterate through the tabs, this uses DOM APIs like querySelectorAll instead of using JSX and React, keeps track of which tabs are active.

[00:05:35] There are some event listeners for clicking and for key down, so you can use both, and then setting the selected tab. So this is kind of the alternative to what JSX and React does for us. A lot is that we don't have to go in and remove attributes manually.

[00:05:56] It makes it really easy to state right there in your template, but a lot of the JavaScript to do this kind of accessibility stuff without a framework. It's totally doable, it's just a little bit more verbose. And so the components of this are keeping track of which tab needs to be selected.

[00:06:15] Here's where the tabindex is getting changed. They're also changing aria-selected, so that the state of it updates true or false, setting and removing of attributes, and this also moves focus. So talking about roving tabindex, right, so only one of these is focusable. I can iterate through here and change all those attributes, but I also have to move focus there.

[00:06:36] So that's part of this pattern, is you change the tabindex on it and then you set focus on to that element. And so this type of pattern you'll see a lot when you get into these types of widgets. And so here's the event handlers. We have an on key down that watches for the arrow keys, and that's how it selects what tabs, kind of depending on where we are in the order.

[00:07:07] And the click event is a little bit simpler, cuz you just click on the one you want. But with arrow keys, we have to actually figure out, well, where are you in the list? What's gonna happen to the right, what's gonna happen to the left, and all that sort of stuff.

[00:07:19] And some of this, we still do in React. You still have to listen for the key events, you still have to keep track of which ones are current. We were doing a lot of that in our examples earlier. But with this, we are manually setting and removing attributes instead of just changing state variables like we were in React.

[00:07:42] Okay, so Tab keys versus the arrow keys. And I think the ARIA Authoring Practices Guide do a really good job of talking about some of the expected keyboard navigation conventions. So you can read up on those if there's other patterns. If you're like, man, this pattern we're working on looks an awful lot like this one in the ARIA Authoring Practices Guide, you could go and see is there some part of those conventions that you could incorporate?

[00:08:14] I do have to say though, do you really need the arrow keys? Probably not. [LAUGH] Sort of touched on that, but I have a screenshot from Costco, they have a big mega menu and they just let it be lists of links. So they didn't go for the whole ARIA menu role thing, I think it works pretty well.

[00:08:33] But you can look at any major e-commerce site and kind of see what approach should they take. And some sites put a lot more effort into accessibility than others. [LAUGH] But it's still fascinating to see what's out there, what are teams doing? What would i work on? What would I love to fix if I worked on that team?

[00:08:52] That's kind of an interesting thought process. Okay, active element, so when we are managing focus, we have a really helpful DOM API called the active element API. This will tell you which element is focused at any given time, and sometimes it can be hard to tell otherwise. If you're debugging focus in a lot of components, maybe there's some that were meant for mobile but they are not actually hidden and you just can't figure out where your focus is cuz it's in a weird state, I have a tool for you.

[00:09:32] So this bit of code, this listener, document.body.addEventListener("focusin", you can add one listener on the body. And then any element that's focused inside of it, you can log to the console using document.activeElement. So let's do that real quick. Let's do it on, Let's see, maybe just this site. So I'm gonna open up the console, and I use this frequently.

[00:10:05] So I'm gonna hit Paste, maybe there's a bookmarker or something, you could do the same thing. But with the console open still, I'm gonna hit Tab and it will tell me, it will log every element that I'm tabbing on. And if it's going through things that are behind a modal or in a layer you can't see, you're gonna get your answer pretty quickly of where your focus was.

[00:10:28] So that can be just in your bag of tricks of debugging techniques. Being able to log the active element is really helpful. It's also helpful If you need to restore focus somewhere. If you open a modal layer and you're writing the code to make that work, you can store what the user's focus was on before they took the action.

[00:10:53] You can store that in a variable, and then you can send them back to the same place. So the activeElement API is kind of the mechanism to keep track of where the user's focus was, or your focus in the event of the debugging we just looked up. And so don't forget visual focus styles, kind of when we're talking about activeElements and trying to figure out which elements are focused using skipped links and targets and all that.

[00:11:25] Visual focus outlines are super important. And so when we're debugging with activeElement, that's one area where it can be kind of useful is to surface. Well, is there an element that I'm focused on that I just can't see for whatever reason? Maybe it's you just don't know where you are on the screen, cuz things are positioned in a weird way and it doesn't have a focus style.

[00:11:48] That can be one pathway to figuring out how to fix it, cuz sometimes it might end up in a weird tab order for some reason and just tabbing through the page isn't enough. That's where this DOM API, it's just one of those tools that you might need someday.