Check / Uncheck all in a Table
TL;DR: Unless you have user testing results saying otherwise, maybe put a check-all checkbox outside the table.
The rest of this post is an awkward mash-up of my posts Don’t Turn a Table into an ARIA Grid Just for a Clickable Row and Check-All / Expand-All Controls with a little bit of Uniquely Labeling Fields in a Table.
Demo
I made a demo. Try it out yourself!
In the Column Header
The biggest challenge to placing the check-all checkbox in the column header is that its accessible name becomes the column header for all other checkboxes in the column. A second challenge is deciding on a visible label (wide column) or not (smaller target size for pointer users and hassle for voice users).
<th>
<input type="checkbox" id="ChkAllColumn">
<span>
<label for="ChkAllColumn">Select all books</label>
</span>
</th>
Consider screen reader users who navigate into the column, whether by table navigation or tabbing into that column for the first time to put focus on a checkbox. What the screen reader exposes (audio or Braille) can be confusing. In VoiceOver on macOS I sometimes got the row checkboxes to announce the wrong state, but could not figure out what caused it each time (hence no bug report).
In the Caption
Putting the check-all checkbox in the <caption> means the table’s name is always going to be at least the accessible name for the checkbox. I say “at least” since you may be tempted to stuff more in the caption.
<caption>
<input type="checkbox" id="ChkAllCaption">
<label for="ChkAllCaption">Select all books</label>
</caption>
A screen reader user can navigate the table without a confusing column header. The check-all checkbox will be in the context of the table. The label is visible for voice control users. But navigating between tables can be annoying when the table name has been co-opted for the checkbox label.
Outside the Table
When outside the table, the check-all checkbox doesn’t risk interfering with table use — table navigation, control announcement, accessible names, checkedness, and so on. The risk is that users unfamiliar with the page may move past it looking for the checkbox in the column header.
<p>
<input type="checkbox" id="ChkAllOutside">
<label for="ChkAllOutside">Select all books</label>
</p>
<table>
How well users recognize the purpose of the check-all checkbox comes down to your design and overall context. Programmatically, there’s a visible label for voice users and no part of the table has been co-opted. Hence, a design exercise.
Wrap-up
Unless you have user testing results saying otherwise, maybe put a check-all checkbox outside the table.
Alternatives may give you a check-all field with a confusing and verbose name or a table with a complex or meaningless name. They may also result in smaller targets or wider columns. Granted, even the pattern I suggest can be a problem for your users, but some of that can be mitigated with training, documentation, context, and / or consistency.
If you have other suggestions (ideally with examples and results of use), toss them into the comments.
16 Comments
I also couldn’t reach the in-caption version by keyboard, at all. (All three are reachable by voice, unsurprisingly.)
In response to . In which browser? In Firefox it comes after the other checkboxes in the table. I probably should have mentioned that.
Out of curiosity, I noticed your table markup doesn’t use
<thead>—is there any reason to positively avoid that tag? (I did watch your Talkin’ Tables talk, and read through some other posts on tables here, but don’t recall anything negative about<thead>, so maybe it was omitted just for convenience and it wouldn’t have added anything semantically?)
In response to . HTML has never required
<thead>, WCAG has never required<thead>, it generally has no impact on users, and misuse can create risk.All that aside,
<thead>has its uses, especially for fixed column headers. Sadly, it took until Chrome 91 and Safari 14 (both in 2021) for the needed CSS support to come.
Is there any solution for the “Select All” issue apart from removing it from the table?
In response to . If you have another place to drop it in the table and can gather testing results on par with what I gathered, then please share!
In response to . Hi Adrian. I looked into the example for ‘In column header’ using NVDA + Firefox and VoiceOver + Safari, and I found out that the Select all header label is not being read for the checkboxes present in the column below. Could you please confirm it or let me know if I am testing in the wrong way.
In response to . Without knowing how you are testing, it’s hard to say. If you are tabbing through the fields, the column header won’t be exposed. If you are navigating the cells only in the column (going down and never leaving it), the column header won’t be exposed. If you’re unfamiliar with how to use NVDA, WebAIM has an intro that briefly covers tables (and also covers VoiceOver if you spend some time there). I link to more training and reference materials elsewhere so you can get more familiar with screen readers (assuming you don’t work with a daily screen reader user).
I tried something recently (for a hobby project though so I have a staggering amount of zero user feedback) where I used a button instead of a checkbox for the “(un)select all”.
So, my header contains a span, with the appropriate heading label, and a button with its label updating depending of the state of selection of my rows.Now, to prevent screen readers to announce the button label, the th has an aria-labelledby attribute pointing to the span.
As far as I can tell the only downside is that, when reading the cell content, the reader targets the first piece of content in the cell, leading it to announce the label twice (once for the aria-labelledby and once for the first piece of content, the span). But that’s really it, as when reading the checkboxes of the rows they are correctly prefixed with the right label once.I’m not a 100% confident having both some text and an interactive element in there is a good pattern, but it looks like it’s doing the job.
I’d be happy to know your though on that as I’m pretty new in the field.
In response to . Any chance you have a URL so I can poke it with a stick / keyboard / screen reader / voice control / WHCM?
In response to . Here’s a minimal reproduction.
By fiddling a bit more, it looks like the behavior I described first only applies to VoiceOver with Safari. VoiceOver with Firefox will always announce both the span and the button, which is a bit of a bummer. But it’s not a default combination either.
In response to . Broadly, you don’t want to use ARIA naming on row / column headers. Sarah Higley pointed out that the spec is unclear and so browsers aren’t required to expose it in any particular way. So your variant results is not surprising.
In quick testing, NVDA / Firefox and JAWS / Chrome announce both regardless of how I navigate into the cells. Which is not wrong. Since those to combos account for the majority of screen reader users, I would look to them for my decision versus Safari / VoiceOver barring an exclusively macOS audience.
Hi Adrian. I looked into the examples, ‘In the Column Header’ you have made and tested it using NVDA + Firefox and VoiceOver + Safari. When I navigated through the checkboxes via the Tab key, the header label was not mentioned in the checkboxes below. Could you please confirm it, or am I just testing it the wrong way?
In response to . Also could you please provide the codebase for the same?
In response to . View-source or inspect the page in the embedded iframe.
Hi Adrian. I looked into the example for ‘In column header’ using NVDA + Firefox and VoiceOver + Safari, and I found out that the Select all header label is not being read for the checkboxes present in the column below. Could you please confirm it or let me know if I am testing in the wrong way.
Leave a Comment or Response