Creating Accessible Tables
Data Tables
Article Contents
- Page 1: Layout Tables
- Page 2: Data Tables
- Marking Up Data Tables
- Designate Row and Column Headers Using the
<th>Tag - Associate the Data Cells with the Appropriate Headers
- Use Proportional Sizing, Rather than Absolute Sizing
- Provide Names or Titles for Data Tables Using the
<caption>Tag - Provide Summaries Using the
summaryAttribute - Avoid Spanned Rows or Columns
- Avoid Tables with More than Two Levels of Row and/or Column Headers
Marking Up Data Tables
Data tables are different from layout tables. The purpose of data tables is to present information in a grid, or matrix, and to have column or rows that show the meaning of the information in the grid. When screen readers read straight through data tables—especially large ones—it's easy for users to get lost.
In order for a data table to be accessible, it must have the proper markup in the HTML. When the proper HTML markup is in place, users of screen readers can navigate through data tables one cell at a time, and they will hear the column and row headers spoken to them.
Designate Row and Column Headers Using the <th> Tag
The very first step toward creating an accessible data table is to designate
row and/or column headers. This is easy enough to do. Most authoring tools
provide a method of changing data cells into header cells. In the markup,
the <td> tag is used for table data cells
and the <th> tag is used for table header
cells. Going back to our original data table example, the column headers
for this table are Name, Age, and Birthday. The
row headers are Jackie and Beth.
| Name | Age | Birthday |
|---|---|---|
| Jackie | 5 | April 5 |
| Beth | 8 | January 14 |
Associate the Data Cells with the Appropriate Headers
Now that we've created headers, we need to associate the cells with the appropriate headers. There are two ways to do associate data cells with their headers.
The scope attribute
The scope attribute should be used on simple data tables such as the one
in this example. Here is the markup for the table, using the scope attribute:
<table border="1" align="center">
<caption>Shelly's Daughters</caption>
<tr>
<th scope="col">Name</th>
<th scope="col">Age</th>
<th scope="col">Birthday</th>
</tr>
<tr>
<th scope="row">Jackie</th>
<td>5</td>
<td>April 5</td>
</tr>
<tr>
<th scope="row">Beth</th>
<td>8</td>
<td>January 14</td>
</tr>
</table>
The scope attribute tells the browser and screen reader that everything
under the column is related to the header at the top, and
everything to the right of the row header is related to that
header. It's a straightforward concept.
The headers and id attributes
Another way to accomplish the same purpose is to use the headers and id attributes.
This method is NOT recommended for simple tables such as the first example.
The headers and id method
should only be used when there is more than one logical level in a table,
and when it is necessary to link more than two headers with a data cell.
If we extend our original example, we can create a table that fits this
criterion. In the table below, data have three headers each, so it is appropriate
to use a more complex technique.
| Name | Age | Birthday | |
|---|---|---|---|
| by birth | Jackie | 5 | April 5 |
| Beth | 8 | January 14 | |
| by marriage | Jenny | 12 | Feb 12 |
The markup looks like this:
<table border="1">
<caption>Shelly's Daughters</caption>
<tr>
<td> </td>
<th id="name">Name</th>
<th id="age">Age</th>
<th id="birthday">Birthday</th>
</tr>
<tr>
<th rowspan="2" id="birth">by birth</th>
<th id="jackie">Jackie</th>
<td headers="birth jackie age">5</td>
<td headers="birth jackie birthday">April 5</td>
</tr>
<tr>
<th id="beth">Beth</th>
<td headers="birth beth age">8</td>
<td headers="birth beth birthday">January 14</td>
</tr>
<tr>
<th id="step">by marriage</th>
<th id="jenny">Jenny</th>
<td headers ="step jenny age">12</td>
<td headers="step jenny birthday">Feb 12</td>
</tr>
</table>
Again, it should be emphasized that this method is more complex. It should
be used with tables of a more complex nature, where the scope attribute
will not work.
Another caveat: spanned rows and columns are not handled well by the JAWS screen reader, which is the most popular brand of screen reader. If at all possible, avoid complex data tables, or represent the data in a way that is less complex, preferably with no more than two headings applying to a single data cell.
Use Proportional Sizing, Rather than Absolute Sizing
The rule that applies to layout tables also applies to data tables. Let the browser window determine the width of the table whenever possible, to reduce the horizontal scrolling required of those with low vision.
Provide Names or Titles for Data Tables
Using the <caption> Tag
Tables ought to have some sort of title or caption to them. This is properly
done by using the caption tag, right after the opening <table> tag,
like this:
<table border="1" align="center">
<caption>Shelly's Daughters</caption>
...
It is not absolutely necessary to have caption tags on every data table
for the sake of accessibility, but it is still a good practice.
Provide Summaries Using the summary Attribute
This guideline is not a requirement for simple tables, but can increase the comprehension of more complex tables for people using screen readers. A complex table of weather data might have a summary that says:
"A warming trend has been observed in Cache Valley, with temperatures about 5 degrees above historical averages over the last two months, with the highest temperature difference being 25 degrees above average."
Such a description would highlight the important elements of a table, and help the user to know what to look for in the data.
Some accessibility advocates insist that ALL tables have summaries, including layout tables. The truth is that this approach is not worth the effort. It is not necessary for screen readers to hear the words "This table used for layout purposes only" at the beginning of every table on a site. Some sites use so many tables, that the added text would only cause confusion, and lengthen the time it takes to listen to the page. A better approach is to only use table summaries when they perform the service that they were designed to perform: to provide brief summaries of complex data.
Avoid Spanned Rows or Columns
Important
The following information pertains to older versions of the JAWS screen reader. Newer versions of JAWS do not have the flaw explained below.
One of the most popular screen readers on the market, JAWS, does not read tables with spanned cells well at all, even when correct markup is used to associate data cells with their corresponding headers. As it turns out, JAWS handles spanned rows in tables very poorly. Let's look at an example of a table that spans more than one row:
The above table uses spanned rows in two instances: for the two cells on the left which contain the department codes BIO and BUS (which stand for biology and business). JAWS gets very confused with these spanned rows, and actually associates the WRONG headers with the data cells below. This means that the user will hear such nonsensical things as "Room number: Tue, Thu" and "Days: 11:00."
Don't despair. There are things that can be done to avoid totally confusing the users of screen readers, even if they are only work-arounds to make up for the programming deficits in JAWS.
Work-around #1: Make duplicate cells within the table, rather than one single row-spanned cell. See what work-around #1 would look like.
Work-around #2: We could also combine the first two columns into one. See what work-around #2 would look like.
Avoid Tables with More than Two Levels of Row and/or Column Headers
There are methods of marking up tables of greater complexity than those discussed here. Some screen readers can read this markup and render the content accurately. The sad truth is that the most popular screen reader, JAWS, cannot do so reliably. If at all possible, use simple, one- or two-dimensional tables without spanned rows or columns. That is the safest bet.