VoiceOver and Tables with an Empty First Header Cell

The Problem

I noticed some interesting behaviour with VoiceOver 3 when working with data tables whose first cell in the first, or header, row is an empty td element. In these cases, VoiceOver does not correctly associate data cells with their proper column th header cells. Instead, VoiceOver seems to shift the header cells one column to the left, such that it will identify a data cell in the second column of the table as belonging to the third column. That is, it will read the th element from the third column as the header for a data cell in the second column. Obviously, this can lead to some serious confusion for the user. Considering the fact that JAWS 11, NVDA 2010.2beta1, and Window-Eyes 7.2 don't exhibit this behaviour makes me think it's a bug with VoiceOver.

An Easy Solution

Fortunately, this is pretty easy to rectify by using a th element instead of a td element for the empty first cell in the header row. Explicitly adding the thead and tbody elements also seems to solve the problem. Update (22 August 2012): Explicity adding thead and tbody solves the issue regarding column headers, but introduces some problem behaviour with row headers. In this case, after moving the VoiceOver cursor to a row header in the first column, VoiceOver (at least in Mountain Lion) will announce the row header cell below it seemingly as the column header for the current row header cell.

What is the Correct Markup in Any Case?

Examples from the Accessibility Community

Looking around the web, it's quite easy to find examples of both, where either a td or a th element is used for an empty first cell in a header row. In fact, on a good number of pages that specifically address the creation of accessible data tables, the use of td is fairly common. You can find examples of these at 456bereastreet.com, WebAIM.org, jimthatcher.com, and usability.com.au. Each of these sites is well-respected in the web accessibility community for its expertise, so it is reasonable to assume that the use of a td element for an empty first cell in a header row is quite proper, if it doesn't also benefit accessibility in some way that I'm not aware of.

Examples from the W3C

On the other hand, many of the relevant examples provided by the W3C, for instance, in Tables in HTML documents from the HTML 4.01 specification, section 4.9 Tabular data from the HTML5 specification, and HTML Techniques for Web Content Accessibility Guidelines 1.0, suggest the use of a th element for the empty first cell in a table. At the same time, WCAG 2.0 Technique H51: Using table markup to present tabular information does the opposite and suggests the use of an empty td element.

No Absolute Answer

So there's some variation out there, and it's not clear to me if only one or both approaches are officially correct. We do know that under the HTML 4.01 specification on th and td elements, td is for data cells, while th is for header cells, and where a cell acts as both header and data, td is to be used. But what is the status of an empty cell that occurs as the first cell in a row of otherwise header cells? Since it is empty, it is not really acting as a column header, but nor is it providing any data, and it certainly is not both.

If I had to choose, and I'm not suggesting that such a choice is necessary, I'd probably come out in favour of using a th element, the issue with VoiceOver notwithstanding. This is because, even if empty, the cell exists in a row of column headers, and not data cells, so it seems reasonable and consistent to consider the empty cell as simply an empty column header.

In the end, however, I'm not sure it really matters, except now for the fact that, bug or not, VoiceOver 3 has a problem when td is used for an empty first cell in a header row, particularly in very simple data tables.

Navigating Tables with VoiceOver

To navigate tables using VoiceOver, press Control+Option+Command+T to move from table to table down the page. Add the Shift key to the previous sequence to move up the page to the previous table. Once you've reached a table, press Control+Option+Shift+Down Arrow to start interacting with it, at which point pressing Control+Option with the arrow keys can be used to move up, down, left, and right through the table cells. To hear the column header for each cell, move left or right through the row.

The Test Tables

In each of the following seven tables there are three columns. The first contains people's names, but the header cell is empty. The second and third columns are for phone number and city name, and their column headers are simple th elements. The first cell in each of the data rows is also a th element. Otherwise, the tables vary in the markup used for the first cell in the header row, the use of thead and tbody elements, and of the scope, id and headers attributes.

Of the seven variations presented, only two seem to enable VoiceOver 3 to properly associate column headers with the data cells. These are Test Table #3, which uses a th element for the empty header cell, and Test Table #5, which implements the thead and tbody elements. In the remaining versions, VoiceOver will identify the cells in the first column, which contain people's names, as having the header "Phone#". The phone number data cells will be associated with the header "City", and city data cells will have no associated header announced.

Note: The tables were tested with VoiceOver 3 in Safari 5.02. Content for the tables is borrowed from the example at http://www.w3.org/TR/WCAG-TECHS/H63.html.

Test Table #1

  • First cell in header row is an empty td
  • Does not use thead or tbody
  • VoiceOver does not properly associate column headers with data cells
Table #1
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #2

  • First cell in header row is a td with a  
  • Does not use thead or tbody
  • VoiceOver does not properly associate column headers with data cells
Table #2
  Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #3

  • First cell in header row is an empty th
  • Does not use thead or tbody
  • VoiceOver does properly associate column headers with data cells
Table #3
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #4

  • First cell in header row is an empty td
  • Uses tbody, but not thead
  • VoiceOver does not properly associate column headers with data cells
Table #4
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #5

  • First cell in header row is an empty td
  • Uses thead and tbody
  • VoiceOver does properly associate column headers with data cells

Update (22 August 2012): Note that explicity adding thead and tbody causes issues with row headers in VoiceOver (tested in Mountain Lion with Safari 6.0) where it will sometimes read the row header from the following row as if it were the column header for the current row reader cell. So this is not really a workable solution.

Table #5
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #6

  • First cell in header row is an empty td
  • Uses scope="col" on the "Phone#" and "City" column header cells
  • Does not use thead or tbody
  • VoiceOver does not properly associate column headers with data cells
Table #6
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

Test Table #7

  • First cell in header row is an empty td
  • Uses id and headers attributes to associate data cells in the "Phone#" and "City" columns with their respective column header cells
  • Does not use thead or tbody
  • VoiceOver does not properly associate column headers with data cells
Table #7
Phone# City
Joel Garner 412-212-5421 Pittsburgh
Clive Lloyd 410-306-1420 Baltimore

5 Responses to VoiceOver and Tables with an Empty First Header Cell

Comments are now closed.