Multi-directional Tables

When a table is more complex or the direction of its headers is not easy to distinguish, it is necessary to mark up the table headers using the <th> element and use the scope attribute to declare the direction of each header. Use the values of row or col of the scope attribute to denote that a header applies to the entire row or column, respectively.

Additionally, you can use the <caption> element to identify the table in a document. This is particularly useful for screen-reader users browsing the web page in “tables mode” where they can navigate from table to table. The Caption & Summary page provides more background and guidance on the using <caption> element.

Table with ambiguous data

In this example the first and last names and cities can’t be distinguished from one another without the appropriate header information. By using the scope element with the value col, the data cells are clearly associated with their header cells “Last Name”, “First Name” and “City”.

Example:
Teddy bear collectors:

Last Name First Name City
Phoenix Imari Henry
Zeki Rome Min
Apirka Kelly Brynn
Code snippet:
<table>
  <tr>
    <th scope="col">Last Name</th>
    <th scope="col">First Name</th>
    <th scope="col">City</th>
  </tr>
  <tr>
    <td>Phoenix</td>
    <td>Imari</td>
    <td>Henry</td>
  </tr>
  <tr>
    <td>Zeki</td>
    <td>Rome</td>
    <td>Min</td>
  </tr>
  <tr>
    <td>Apirka</td>
    <td>Kelly</td>
    <td>Brynn</td>
  </tr>
</table>

Table with header cells in one column only

The following tables of countries and their capital cities has the first column with the country names marked up as <th> cells. It needs the scope attribute with the value of row to declare the applicability of each header cell. Otherwise some screen readers will associate header cells with other header cells because they are in the same column. For example in the cell marked “Holland” some screen readers will read “Belgium – France – Holland” if the scope attribute is not provided.

Example:
Capital cities
Belgium Brussels
France Paris
Holland Amsterdam
Luxembourg Luxembourg
Spain Madrid
UK London

Further improvement

Building on the example above, adding a header row can help to make the table even clearer and describe the content of the data cells better.

Example:
Capital cities
Country Capital city
Belgium Brussels
France Paris
Holland Amsterdam
Luxembourg Luxembourg
Spain Madrid
UK London
Code snippet:
<table>
  <caption>Capital cities</caption>
  <tr>
    <th scope="col">Country</th>
    <th scope="col">Capital city</th>
  </tr>
  <tr>
    <th scope="row">Belgium</th>
    <td>Brussels</td>
  </tr>
  <tr>
    <th scope="row">France</th>
    <td>Paris</td>
  </tr>
  <tr>
    <th scope="row">Holland</th>
    <td>Amsterdam</td>
  </tr>
  […]
</table>

Full code Example “Table with header cells in one column only”

Table with header cells in the top row and first column

The following table of opening times has header information in both the top row and the first column. All header cells are marked up as <th> cells with scope attributes added.

In the header row, the col value for scope associates each header cell with the data cells in the column. In the header column, the row value associates the individual headers with their rows.

Example:

Delivery slots:

Monday Tuesday Wednesday Thursday Friday
09:00 - 11:00 Closed Open Open Closed Closed
11:00 - 13:00 Open Open Closed Closed Closed
13:00 - 15:00 Open Open Open Closed Closed
15:00 - 17:00 Closed Closed Closed Open Open
Code snippet:
<table>
  <tr>
    <td></td>
    <th scope="col">Monday</th>
    <th scope="col">Tuesday</th>
    <th scope="col">Wednesday</th>
    <th scope="col">Thursday</th>
    <th scope="col">Friday</th>
  </tr>
  <tr>
    <th scope="row">09:00 - 11:00</th>
    <td>Closed</td>
    <td>Open</td>
    <td>Open</td>
    <td>Closed</td>
    <td>Closed</td>
  </tr>
  <tr>
    <th scope="row">11:00 - 13:00</th>
    <td>Open</td>
    <td>Open</td>
    <td>Closed</td>
    <td>Closed</td>
    <td>Closed</td>
  </tr>
  […]
</table>

Full code for “Table with header cells in the top row and first column”

Table with an offset column of header cells

In this table, the row header cells are in the second rather than in the first column. The approach is very similar to the examples above: The scope of the header cells in the top row is set to col. By using the row value for scope assigns the header cells in the second column to data cells on the left and the right of the individual header cell.

Example:
Holidays taken in the last six months
ID Name July August September October November December
215 Abel 5 2 0 0 0 3
231 Annette 0 5 3 0 0 6
173 Bernard 2 0 0 5 0 0
141 Gerald 0 10 0 0 0 8
99 Michael 8 8 8 8 0 4
Code snippet:
[…]
<tr>
  <td>215</td>
  <th scope="row">Abel</th>
  <td>5</td>
  <td>2</td>
  <td>0</td>
  <td>0</td>
  <td>0</td>
  <td>3</td>
</tr>
[…]

Full code for “Table with an offset column of header cells”