7. Row and column headers shall be identified for data tables.
(508:G. W3C: 5.1 Priority 1)
8. Markup shall be used to associate data cells and header cells for data tables that
have two or more logical levels of row or column headers. (508:H. W3C: 5.2 Priority 1)
A simple data table created with proper data table mark up, might look like this:
| Column 1 header | Column 2 header | |
|---|---|---|
| Row 1 header | Column 1 Row 1 | Column 2 Row 1 |
| Row 2 header | Column 1 Row 2 | Column 2 Row 2 |
The code that created this table is below.
<caption>Example of a simple data table created using HTML markup.</caption>
<tr>
<td></td>
<th>Col. 1 header</th>
<th>Col. 2 header</th>
</tr>
<tr>
<th>Row 1 header</th>
<td>C1R1</td>
<td>C1R2</td>
</tr>
<tr>
<th>Row 2 header</th>
<td>C2R1</td>
<td>C2R2</td>
</tr>
</table>
The above example is preferable to the using the <PRE> element to layout the data, because future browsers will use the TH and other TABLE markup to logically linearize tables.
Note: if you are using tables for page layout (instead of CSS), then you should NOT use markup reserved for data tables (like TH, HEADER, SCOPE, COLGROUP, etc.) because those elements will be used by some agents to identify and manipulate data).
For "complex" tables, i.e. where tables have structural divisions beyond those implicit in the rows and columns, use appropriate markup to identify those divisions.
Example: A travel expenses worksheet. While the following data table appears simple enough visually, it would be difficult to understand if read by some of today's screen-readers. A good way to approximate what some screen-reader users will hear is to hold a ruler to the table, and read straight across the screen. Then, move the ruler down until the next line of characters is displayed. Read straight across. Acter a while, pick a data cell at random and, without looking at the column or row title, try and remember what headers describe that data point. The larger and more complex the table, the harder it would be to remember the row and column relationships.
TRIP, |
Meals | Room | Trans. | Total |
|---|---|---|---|---|
| San Jose | ||||
| 25 Aug 97 | 37.74 | 112.00 | 45.00 | |
| 26 Aug 97 | 27.28 | 112.00 | 45.00 | |
| Subtotal | 65.02 | 224.00 | 90.00 | 379.02 |
| Seattle | ||||
| 27 Aug 97 | 96.25 | 109.00 | 36.00 | |
| 28 Aug 97 | 35.00 | 109.00 | 36.00 | |
| Subtotal | 131.25 | 218.00 | 72.00 | 421.25 |
| Totals | 196.27 | 442.00 | 162.00 | 800.27 |
Today, most users of screen-readers would hear this table read as:
TRAVEL EXPENSES (actual cost, US$)
TRIP,
Meals Room Trans Total
date
San Jose
25 Aug 97 37.74 112.00 45.00
26 Aug 97 27.28 112.00 45.00
Subtotal 65.02 224.00 90.00 379.02
Seattle
27 Aug 97 96.25 109.00 36.00
28 Aug 97 35.00 109.00 36.00
Subtotal 131.25 218.00 72.00 421.25
Totals 196.27 442.00 162.00 800.27
Try reading this out loud to yourself and when you get to the 11th line, try to guess what the meaning of the fourth value is supposed to be... without looking back at the header information.
Tomorrow's smart browsers or screen-readers will use additional HTML 4.0 markup (TBODY, THEAD, SCOPE, HEADERS, etc.) to "intelligently" decode a table for speech or alternative output. This table might be read as follows:
TRAVEL EXPENSES (actual cost, US$)
Trip: San Jose, Date: 25 Aug 97, Meals: 37.74, Room: 112.00, Trans. 45.00
Trip: San Jose, Date: 26 Aug 97, Meals: 27.28, Room: 112.00, Trans. 45.00
Trip: San Jose, Subtotal, Meals: 65.02, Room: 224.00, Trans. 90.00, Total: 379.02
Trip: Seattle, Date: 27 Aug 97, Meals: 96.25, Room: 109.00, Trans. 36.00
Trip: Seattle, Date: 28 Aug 97, Meals: 35.00 Room: 109.00, Trans. 36.00
Trip: Seattle, Subtotal, Meals: 131.25, Room: 218.00, Trans. 72.00, Total: 421.25
Trip: Totals: Meals: 196.27, Room: 442.00, Trans: 162.00, Total: 800.27
This code shows one way this table might be marked up to allow the greatest access:
<caption>TRAVEL EXPENSES (actual cost, US$)</caption>
<thead>
<tr>
<th><p><span id="t1-r1-l1">TRIP</span>,<br>
<span id="t1-r1-l2"> date</span></P></th>
<th scope="column">Meals</th>
<th scope="column">Room</th>
<th scope="column"><abbr="Transportation">Trans.</abbr></th>
<th scope="column">Total</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="rowgroup" headers="t1-r1-l1">San Jose</th>
</tr>
<tr>
<td scope="row" headers="t1-r1-l2"> 25 Aug 97</td>
<td>37.74</td>
<td>112.00</td>
<td>45.00</td>
</tr>
<tr>
<td scope="row" headers="t1-r1-l2"> 26 Aug 97</TD>
<td>27.28</td>
<td>112.00</td>
<td>45.00</td>
</tr>
<tr>
<td scope="row">Subtotal</td>
<td>65.02</td>
<td>224.00</td>
<td>90.00</td>
<td>379.02</td>
</tr>
</tbody>
<tbody>
<tr>
<th scope="rowgroup" headers="t1-r1-l1">Seattle</th>
</tr>
<tr>
<td scope="row" headers="t1-r1-l2"> 27 Aug 97</td>
<td>96.25</td>
<td>109.00</td>
<td>36.00</td>
</tr>
<tr>
<td scope="row" headers="t1-r1-l2"> 28 Aug 97</td>
<td>35.00</td>
<td>109.00</td>
<td>36.00</td>
</tr>
<tr>
<td scope="row">Subtotal</td>
<td>131.25</td>
<td>218.00</td>
<td>72.00</td>
<td>421.25</td>
</tr>
</tbody>
<tbody>
<tr>
<th scope="row">Totals</th>
<td>196.27</td>
<td>442.00</td>
<td>162.00</td>
<td>800.27</td>
</tr>
</tbody>
</table>



