Skip to main content


Asking for JavaScript WebDev Accessibility help
I make games. I often have the requirement to quickly build a dialog or a window of some kind. Using native HTMl here makes sense to me, so I'm implementing a bunch of components that implement the most common features I need in my UI.
Now they have to be accessible, so read well with screen readers, and be fully keyboard navigable. This includes things like lists, tab bars, menus, etc.
So here's the problem. I have some things that need to be inside a container. A list for example. So you have the list container, and then the list items inside it.
When you tab around, I want the container to be tabbable, not the list item. So you don't tab through the list, you tab to the list, and then use the arrows to move around.
Now here's the problem. When the container is tabbable, and not the list item, when you tab to the container, it either:
* reads something like "List title section" and then nothing, not even the item you have selected. Or
* Reads all the list items at once.
Either of those are not great obviously. Ideally, I'd like it to read the list title, then list, and then the selected item.
So the way I get it to do this is by detecting when you tab/focus the list container, and then immediately set the focus to the selected list item instead.
Now this works fantastic. You can tab around, and it automatically puts you right on the list item you have selected, and it even gets read.
But somehow, when you don't just tab around, but also shift tab around, this shift tab lands you back on the list container. And that automatically moves your focus back inside the list. So effectively, once you're in a list, you're trapped.
Does anyone have an idea how to get around this without doing ugly hacks like stealing tab and shift tab and implementing tab order myself? I want to use as many native browser features as possible so if there's another way to do this, please feel free to tell me.
#HTML #JavaScript #accessibility

Pitermach reshared this.

in reply to Talon

Asking for JavaScript WebDev Accessibility help
It's funny. I've done web accessibility for quite a while now, but this is the first time I'm trying to implement a user interface which you use completely without making use of special "browse mode" or "virtual cursor" features at all.
I feel rather silly for being stuck. :drgn_blush:
in reply to Talon

Asking for JavaScript WebDev Accessibility help
I have a few ideas, but they all sound... rather meh.
I could re-implement tab order manually. This would probably make it easier to hook up navigation to a game pad in the future, and I would want this, so maybe I'll have to do this anyway.
I could set the aria title/label of the list container to whichever list item you have selected. That way, when you focus the container, the screen reader will automatically read the right info. But this will still cause it to say "section" at the end, and that's rather ugly and I don't know how I feel about that.
in reply to Talon

Asking for JavaScript WebDev Accessibility help
Stupid question, but legitimate curiosity. Why are we assuming screen reader users are tabbing around? I mean obviously I can't speak for everyone but tab/shift+tab is kind of a last resort for me if literally nothing else works.
in reply to James H

Asking for JavaScript WebDev Accessibility help
@quanin if you're playing a game and this game requires keyboard input, you're in focus mode already. So virtual cursor stuff is disabled. So it would be nice if you could use the entire UI without having to mess with it.
Also, let's say we have a list. That list has different items. If the keyboard is free of web navigation keys, you could use the arrows, page up/down, or even search to filter the list, and once you have an item, you could press a key to trigger an action immediately on that item.
Let's imagine you're in a list of spells. You arrow around to find a spell. You press i, and it immediately reads out all the relevant info of that spell. Description, mana cost, cooldown, etc.
in reply to James H

Asking for JavaScript WebDev Accessibility help
@quanin that isn't to say that you can't use the UI any other way. It's still normal DOM, so everything is still available to you. Virtual cursors, object navigation, whatever your screen reader has. So it would be additional, not exclusive.
in reply to Talon

Asking for JavaScript WebDev Accessibility help
I wonder if dropdowns/comboboxes would be a better idea for what you're looking for. You can code them in such a way that the game only reacts to a selected option if you smack enter on it.
in reply to James H

Asking for JavaScript WebDev Accessibility help
@quanin I do have combos, but they wouldn't work in all situations. Like the one I described.
in reply to Talon

Asking for JavaScript WebDev Accessibility help
Can you show some HTML/JavaScript code? Maybe on codepen.io?
in reply to Mikołaj Rotnicki

Asking for JavaScript WebDev Accessibility help
@rotnicki right. It's part of a larger codebase, so I'm working on a reproduceable sample to show what I mean.
in reply to Talon

Asking for JavaScript WebDev Accessibility help

Good. So please show some code samples when you are ready.

@talon

in reply to Mikołaj Rotnicki

Asking for JavaScript WebDev Accessibility help
@rotnicki sorry, codepen is not very accessible. I hope I did this right.
codepen.io/TalonTheDragon/pen/…
in reply to Talon

Set the tabindex of the container to -1 when one of the child elements have focus, and back to 0 when they don’t.
in reply to Steve

@sclower oh that is clever! I will try this! I didn't think of that! Thank you that might work! Let's see.
in reply to Talon

No prob. I’ve implemented this pattern before. As long as you have a reliable way of tracking whether your list children have focus, it’ll work.
in reply to Talon

Asking for JavaScript WebDev Accessibility help
I would say container does not have to have tabIndex set at all.
Active aka selected item should always have tabIndex="1" all other items should have tabIndex="-1" and you have to implement your own cursor handling. This technique is called rovingTabIndex. This is usefull while creating treeview, listbox, popup menu, menu bar and whatever widget where navigating around controls inside a container. See this paragraph or the whole article for more details w3.org/WAI/ARIA/apg/practices/…
in reply to Peter Vágner

Asking for JavaScript WebDev Accessibility help
@pvagner Ah. Yeah, this is similar to what @sclower suggested and I'm looking into this. Thanks so much!
in reply to Peter Vágner

Asking for JavaScript WebDev Accessibility help
@pvagner this works beautifully. Thanks so much! :dragon_heart:
in reply to Peter Vágner

Asking for JavaScript WebDev Accessibility help
@pvagner Please don't ever use `tabindex="1"`, as this forces the element to occupy the first position in the tab order regardless of where it is actually located on the page. `tabindex="0"` works just as well, without the same issue.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@jscholes @pvagner yeah, I set tabindex to 0 for the active element.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@James Scholes Yes, I must have mixed it. Thanks for the correction. With help from other comments and the link @Talon managed to do the right think luckily.
in reply to Talon

Asking for JavaScript WebDev Accessibility help

You are fighting against the semantics that were provided for this purpose. If you want a listbox, build a listbox.

But, to answer your specific question, you could use a roving tabindex on your list items, so that only one of them is in the tab order at all times. Let's say the first list item has `tabindex="0"`, the list has an accessible name via `aria-label` or `aria-labelledby`, and the list is not in the tab order.

Users will tab to the first item, and the screen reader will hopefully announce the name and role of the list, plus the item they just tabbed to. When they hit Up/Down Arrow or other navigation keys, set `tabindex="-1" on the item they just came from, and `tabindex="0"` on the destination.

Note: you may need to explicitly add `role="list"` to the `<ul>`, or VoiceOver may just decide to ignore the semantics. This also may not be a good idea in general. As a point of reference, Slack's messages list uses this approach.

in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@jscholes that is what I'm trying to build. But there are no native listbox elements in HTML, and w3c suggests the roving tab index solution which works well.
Listboxes are built using normal unordered lists and list items, so you have to implement this yourself. And tabbing into the list is where my trouble was. Roving tab index does exactly what I wanted.
Unless I'm completely wrong here.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
Once you've done all of that, though, you may as well just add `role="listbox"` to the `<ul>`, and `role="option"` to each `<li>`. The only reason Slack doesn't use a listbox role is that they need to override the accessible name of each message node, and `role="option"` apparently struggles with that in some screen readers.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@jscholes does setting the role automatically let a screen reader navigate through the list using arrows? I was under the impression that it didn't.
in reply to Talon

Asking for JavaScript WebDev Accessibility help
Definitely not. Just saying that if you've built the underlying structure, added arrow key and other keyboard support, and written the code to manage `tabindex`, you may as well ad the listbox and option roles at that point. It will give some screen reader users more recognisable announcements than an interactive list without them, particularly for JAWS.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@jscholes oh absolutely. I did. It also helped a lot with FireFox where text items in a list didn't read properly when arrowed through without the roles.
in reply to James Scholes

Asking for JavaScript WebDev Accessibility help
@jscholes yup, just tried. You still have to implement keyboard navigation manually, as well as set roving tab index, otherwise when you tab to the list, the selected item is not read.