Column Headers and Browser Support
Data tables need column headers.
What they probably do not need is a new set of column headers every few rows, particularly not when they change the meaning of the data.
Sometimes an organization has chosen a library or framework that gives them little control over column headers. As a result, they may jam column headers into more uncomfortable parts of a table in order to meet a requirement, even though the total impact of that decision may be unknown to them.
Then I come along and test these weird constructs. I also log them so I can write posts like this to show variations of what I see in the wild and how they fall down in assistive technology such as screen readers.
As developers get angry at screen reader support for HTML features, I regularly have to remind them that most “support” comes from the browser. Tables are a great example of this in action.
The results of testing these examples demonstrate that clearly. As such, I group the outcomes by browser, where you will see Chrome / Chromium handles these the best. Firefox and Safari perform worst. I throw in IE11 as a comparison point.
Please note that if I say a construct works fine, that does not necessarily mean it works the way you may want. What I am saying is it conveys the text that is shown in a probably sufficient way. If the HTML is invalid but I say it works, that is because I care more about user experience than technical purity. If the HTML is valid but the implementation falls down, then we are in bug territory.
Test kit and a standalone set of examples are at the end of the post.
1. Column Headers at End of Table
All the <th>
es live in the last row of the table. There is no <thead>
in the table. This is valid HTML.
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
Author | Title | Year | ISBN-13 |
---|
Chrome / Chromium browsers perform best:
- Chrome
- with NVDA: works fine.
- with JAWS: works fine.
- with macOS VoiceOver: works fine.
- with TalkBack: does not announce column header.
- Edge
- with Narrator: works fine.
- Firefox
- with NVDA: does not announce column header.
- with JAWS: does not announce column header.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: does not announce column header.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: does not announce column header.
- with iOS VoiceOver: does not announce column header.
- IE11
- with JAWS: does not announce column header.
- with NVDA: does not announce column header.
2. Column Headers in <thead>
at End of Table
All the <th>
es live in the last row within a <thead>
. Visually this is rendered as the first row of the table.
Note that it is invalid to place a <thead>
as a following-sibling of <tbody>
within a <table>
(as I do in this example):
As a child of a
table
element, after anycaption
, andcolgroup
elements and before anytbody
,tfoot
, andtr
elements, but only if there are no otherthead
elements that are children of thetable
element.
Despite this invalid construct, I tested it anyway.
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
Author | Title | Year | ISBN-13 |
---|
- Chrome
- with NVDA: works fine.
- with JAWS: works fine.
- with macOS VoiceOver: works fine.
- with TalkBack: works fine.
- Edge
- with Narrator: works fine.
- Firefox
- with NVDA: works fine.
- with JAWS: does not announce column header.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: could not get into table at all.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: works fine.
- with iOS VoiceOver: works fine.
- IE11
- with JAWS: does not announce column header.
- with NVDA: does not announce column header.
3. Column Headers Mid-Table
All the <th>
es live in the middle of the table. There is no <thead>
in the table. This is valid HTML.
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Author | Title | Year | ISBN-13 |
---|---|---|---|
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
- Chrome
- with NVDA: works fine.
- with JAWS: works fine.
- with macOS VoiceOver: works fine.
- with TalkBack: does not announce column header.
- Edge
- with Narrator: works fine.
- Firefox
- with NVDA: only announces headers in cells that follow the headers in source.
- with JAWS: only announces headers in cells that follow the headers in source.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: only announces headers in cells that follow the headers in source.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: does not announce column header.
- with iOS VoiceOver: does not announce column header.
- IE11
- with JAWS: only announces headers in cells that follow the headers in source.
- with NVDA: does not announce column header.
4. Changing Column Headers Mid-Table
There are <th>
es in the first row, and then more <th>
es in the middle of the table. There is no <thead>
in the table. This is valid HTML.
Author | Title | Year | ISBN-13 |
---|---|---|---|
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Hammer | Sandwich | Taco | Wrench |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
Note that I consider announcing both column headers for a single cell to be good, as well as announcing only the column header that most closely precedes the cell. Whether you think either or both is good depends on your use case and expectation.
- Chrome
- with NVDA: announces both column headers in order of source.
- with JAWS: announces both column headers in order of source.
- with macOS VoiceOver: announces both column headers in order of source.
- with TalkBack: ignores second set of column headers.
- Edge
- with Narrator: announces both column headers in order of source.
- Firefox
- with NVDA: announces only column header that most closely precedes cell in source.
- with JAWS: announces only column header that most closely precedes cell in source.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: announces all column headers that precedes cell in source.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: ignores second column header in each column.
- with iOS VoiceOver: ignores second column header in each column.
- IE11
- with JAWS: announces only column header that most closely precedes cell in source.
- with NVDA: ignores second column header in each column.
5. Changing Column Headers Mid-Table and End-Table
There are <th>
es in the first row, and then more <th>
es in the middle of the table, and again in the last row. There is no <thead>
in the table. This is valid HTML.
Author | Title | Year | ISBN-13 |
---|---|---|---|
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Hammer | Sandwich | Taco | Wrench |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
Monday | Tuesday | Wednesday | Thursday |
Note that I consider announcing all three column headers for a single cell to be good, as well as announcing only the column header that most closely precedes the cell. Whether you think either or both is good depends on your use case and expectation.
- Chrome
- with NVDA: announces all three column headers in order of source.
- with JAWS: announces all three column headers in order of source.
- with macOS VoiceOver: announces all three column headers in order of source.
- with TalkBack: ignores second and third set of column headers.
- Edge
- with Narrator: announces all three column headers in order of source.
- Firefox
- with NVDA: announces only column header that most closely precedes cell in source.
- with JAWS: announces only column header that most closely precedes cell in source.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: announces all column headers that precedes cell in source.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: ignores second and third column header in each column.
- with iOS VoiceOver: ignores second and third column header in each column.
- IE11
- with JAWS: announces only column header that most closely precedes cell in source.
- with NVDA: ignores second and third column header in each column.
6. Column Headers in <tfoot>
at End of Table
The <th>
es live in the <tfoot>
in the last row. This is valid HTML.
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
Author | Title | Year | ISBN-13 |
---|
- Chrome
- with NVDA: works fine.
- with JAWS: works fine.
- with macOS VoiceOver: works fine.
- with TalkBack: does not announce column header.
- Edge
- with Narrator: works fine.
- Firefox
- with NVDA: does not announce column header.
- with JAWS: does not announce column header.
- with Narrator: does not announce column header; announces “edit”.
- with macOS VoiceOver: does not announce column header.
- with TalkBack: does not announce column header; does not announce row or column position.
- Safari
- with macOS VoiceOver: does not announce column header.
- with iOS VoiceOver: does not announce column header.
- IE11
- with JAWS: does not announce column header.
- with NVDA: does not announce column header.
7. Column Headers in <tfoot>
at Start of Table
All the <th>
es live in the first row within a <tfoot>
. Visually this is rendered as the last row of the table.
Note that it is invalid to place a <tfoot>
as a preceding-sibling of <tbody>
within a <table>
(as I do in this example):
As a child of a
table
element, after anycaption
,colgroup
,thead
,tbody
, andtr
elements, but only if there are no othertfoot
elements that are children of thetable
element.
Despite this invalid construct, I tested it anyway.
Author | Title | Year | ISBN-13 |
---|---|---|---|
Murasaki Shikibu (紫 式部, Lady Murasaki) |
The Tale of Genji (源氏物語, Genji monogatari) |
1021 | 9780142437148 |
Miguel De Cervantes | The Ingenious Gentleman Don Quixote of La Mancha | 1605 | 9783125798502 |
Gabrielle-Suzanne Barbot de Villeneuve | La Belle et la Bête | 1740 | 9781910880067 |
Sir Isaac Newton | The Method of Fluxions and Infinite Series: With Its Application to the Geometry of Curve-lines | 1763 | 9781330454862 |
Mary Shelley | Frankenstein; or, The Modern Prometheus | 1818 | 9781530278442 |
Herman Melville | Moby-Dick; or, The Whale | 1851 | 9781530697908 |
Emma Dorothy Eliza Nevitte Southworth | The Hidden Hand | 1888 | 9780813512969 |
F. Scott Fitzgerald | The Great Gatsby | 1925 | 9780743273565 |
George Orwell | Nineteen Eighty-Four | 1948 | 9780451524935 |
Nnedi Okorafor | Who Fears Death | 2010 | 9780756406691 |
- Chrome
- with NVDA: works fine.
- with JAWS: works fine.
- with macOS VoiceOver: works fine.
- with TalkBack: does not announce column header.
- Edge
- with Narrator: works fine.
- Firefox
- with NVDA: does not announce column header; virtual cursor starts at last row.
- with JAWS: works fine; virtual cursor starts at last row.
- with Narrator: does not announce column header; announces “edit”; virtual cursor starts at last row.
- with macOS VoiceOver: could not get into table at all.
- with TalkBack: does not announce column header; does not announce row or column position; virtual cursor starts at last row.
- Safari
- with macOS VoiceOver: does not announce column header.
- with iOS VoiceOver: does not announce column header.
- IE11
- with JAWS: does not announce column header.
- with NVDA: works fine; virtual cursor starts at last row.
More Info
These results can change over time as browsers and screen readers update. Other content on your own page or in the tables may interact in ways this does not cover. These are minimal HTML examples so be prepared to test with constructs that use lots of nested HTML. This does not account for data grids made from table
or grid
roles. Please see a physician if itching continues but probably enjoy the swelling in the meantime.
My testing suite:
- Firefox 95–97 / NVDA 2021.2
- Chrome 97–100 / JAWS 2021–2022
- Edge 97–98 / Narrator / Windows 10
- Safari 15.2 / VoiceOver / macOS 12.1
- Safari / VoiceOver / iPadOS 15.1
- Chrome 96–97 / TalkBack 9.1 / Android 12
- Firefox 96–98 / TalkBack 9.1 / Android 12
Examples
These tables exist as a CodePen that you can try outside of the context of my site, also in a cruft-free debug mode.
See the Pen Column Header Placement Tests by Adrian Roselli (@aardrian) on CodePen.
8 Comments
Great write up as always! As you explained you accepted both/all headers being read as a valid result but I wish to emphasize for any future readers that unfortunately anything else seems to be impossible for Chrome + NVDA and Edge + Narrator. The explicit headers attribute seems to be totally ignored and all previous column headers are being announced when you advance in the table. This can make it really annoying to go through long tables with several header rows as when you are nearing the end of the table the amount of headers being announced can really stack up. So always divide your multi-header tables if possible! :)
In response to .Sampo, while this post does not use the
headers
attribute at all (it focuses only on<th>
for columns and how those are exposed), in my cart table post I link to this Chromium bug which details the issue you raise: Issue 1081201: headers attribute ignored in tables resulting in an incorrect screen reader experience (accessibility)As for the text of column headers (from
<th>
es) stacking up, I agree it can get very annoying for screen reader users. My counsel has almost always been to revisit the overall stuff you are trying to convey. I had a whole paragraph about bankers trying to re-create their wonky multi-headed Excel files on the web, but cut it due to rambling.
FWIW I am seeing (hearing) with an “ordinary” HTML table (i.e.)
<table>
<caption>N</caption>
<thead>
<tr>
<th scope="col">N</th>
<th scope="col">N</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">N</th>
<td>N</td>
</tr>
</tbody>
</table>and NVDA, navigating with Ctrl + Alt + arrows, at row change row TH content is announced, but at column change column TH content is not announced. Weird.
Win + Firefox or Chrome.
PS: hope my entified table up there works…
In response to .Alan, WordPress comments do not like <pre>, even when I add them. Otherwise your code came through fine.
I copied your table and tried it (while also adding more rows and columns, but changing the headers to be distinct) and I had no issues in NVDA 2024.2 with Firefox 128. If you have a live example, that would be easier to try since we can both run at the same code.
Thanks Adrian. Yes, here’s a table.
My versions are: NVDA 2023.3 & Firefox 115.14.
So maybe this is a (somewhat) stale NVDA or Firefox thing…
Just upgraded NVDA to 2024.2 and the issue remains.
I can’t upgrade the browser here, but I’ll try on ano m/c later. Intriguing.
In response to .Using NVDA 2024.2 and Firefox 129, I hear the column headers as I navigate between columns in the table and row headers as I navigate between rows. (Ctrl + Alt + arrow keys).
In response to .OK, thanks. So it’s either my ver of Win or Firefox. I’ll try to see which.
Leave a Comment or Response