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!

Cruft-less version.

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).

This video shows VoiceOver on iPadOS first putting focus on the select-all checkbox. Then it jumps ahead to showing explore-by-touch to put focus on three different checkboxes in the first column. I used a circuitous route with my finger just to demonstrate how a first visit into the column with the checkbox can make for a verbose and potentially confusing name.

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.

The video starts with the JAWS element navigator open. I select tables and it tells me there are three tables on the page. Two of them have useful names, but “Select all books” is not a good name to distinguish it from other tables. Navigating to the table also announces “Select all books” as its name, also not terribly helpful for identifying its purpose.

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.

The video starts by navigating directly to the table in NVDA. The “Select all” checkbox is not within the table, so as I move through the fields in the table I don’t know it’s there. If I move backward out of the table, then I encounter it. I navigate back to the table to demonstrate its relationship.

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

Reply

I also couldn’t reach the in-caption version by keyboard, at all. (All three are reachable by voice, unsurprisingly.)

deborah kaplan; . Permalink
In response to deborah kaplan. Reply

In which browser? In Firefox it comes after the other checkboxes in the table. I probably should have mentioned that.

Reply

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 Pete. Reply

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.

Reply

Is there any solution for the “Select All” issue apart from removing it from the table?

Vivek; . Permalink
In response to Vivek. Reply

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 Adrian Roselli. Reply

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.

Vivek Pinto; . Permalink
In response to Vivek Pinto. Reply

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).

Reply

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.

Pierre; . Permalink
In response to Pierre. Reply

Any chance you have a URL so I can poke it with a stick / keyboard / screen reader / voice control / WHCM?

In response to Adrian Roselli. Reply

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.

Pierre; . Permalink
In response to Pierre. Reply

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.

Reply

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?

Vivek Pinto; . Permalink
In response to Vivek Pinto. Reply

Also could you please provide the codebase for the same?

Vivek Pinto; . Permalink
In response to Vivek Pinto. Reply

View-source or inspect the page in the embedded iframe.

Reply

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.

Vivek Pinto; . Permalink

Leave a Comment or Response

  • The form doesn’t support Markdown.
  • This form allows limited HTML.
  • Allowed HTML elements are <a href>, <blockquote>, <code>, <del>, <em>, <ins>, <q>, <strong>, and maybe some others. WordPress is fickle and randomly blocks or allows some.
  • If you want to include HTML examples in your comment, then HTML encode them. E.g. <code>&lt;div&gt;</code> (you can copy and paste that chunk).