How to Improve HTML Table Accessibility with Markup
Web accessibility refers to designing web applications in a way that it can be used with ease by people with visual disabilities. Some of these users rely on screen readers to read out the content in the web pages. The screen readers interpret the code present on the page and read out its content to the user.
<table>
is a widely used HTML element to display data in a structured fashion in webpages. Its design ranges from simple ones to complex ones, complete with row headers, merged headers etc.
If a table is not designed with accessibility in mind, it will be difficult for the screen readers to translate the data in complex tables meaningfully for the users. Hence, to make complex HTML tables more easily understandable, for accessibility, we must ensure that the headers, column groups, row groups, etc. are clearly defined. We’ll see below how this is achieved in markup.
The Scope Attribute
Even for a simple table with <th>
tag that clearly defines the headers, you can improve its accessibility with the scope
attribute and not give way to any confusion that may arise from similar types of data in the cells.
<table> <tr> <th scope="col">Employee Name</th> <th scope="col">Employee Code</th> <th scope="col">Project Code</th> <th scope="col">Employee Designation</th> </tr> <tr> <td>John Doe</td> <td>32456</td> <td>456789</td> <td>Director</td> </tr> <tr> <td>Miriam Luther</td> <td>78902</td> <td>456789</td> <td>Deputy Director</td> </tr> </table>
What does scope attribute do? According to W3C:
Note: This attribute specifies the set of data cells for which the current header cell provides header information.
In other words it helps us associate the data cells with their corresponding header cells.
Please note that in the above example you can switch <th>
for <td>
. As long as its scope
has the value col
, it will be interpreted as the corresponding column’s header.
The scope
attribute can have any one of these four values; col
, row
, rowgroup
, colgroup
to refer to a column’s header, a row’s header, a group of columns’ header and a group of rows’ header respectively.
Complex Tables
Now let us move on to a more complex table.
Above is a table that lists students in a class and their grades in practical and theory for three subjects.
Here is the HTML code for it. The table has used rowspan
and colspan
to create merged headers for the data cells.
<table> <tr> <th rowspan="2">Student Name</th> <th colspan="2">Biology</th> <th colspan="2">Chemistry</th> <th colspan="2">Physics</th> </tr> <tr> <th>Practical</th> <th>Theory</th> <th>Practical</th> <th>Theory</th> <th>Practical</th> <th>Theory</th> </tr> <tr> <th>John Doe</th> <td>A</td> <td>A+</td> <td>B</td> <td>A</td> <td>A</td> <td>A-</td> </tr> <tr> <th>Miriam Luther</th> <td>A</td> <td>A</td> <td>C</td> <td>C+</td> <td>A</td> <td>A-</td> </tr> </table>
In the above table, each data cell, that is each of the table cells displaying the grade, is associated with three pieces of information:
- Which student does this grade belong to?
- Which subject does this grade belong to?
- Is this grade for the Practical or Theory section?
Those three information are defined in three different types of header cells structurally and visually:
- Student name
- Subject name
- Practical or theory
Let’s define the same for accessibility.
<table> <col> <colgroup span="2"></colgroup> <colgroup span="2"></colgroup> <colgroup span="2"></colgroup> <tr> <th rowspan="2" scope="col">Student Name</th> <th colspan="2" scope="colgroup">Biology</th> <th colspan="2" scope="colgroup">Chemistry</th> <th colspan="2" scope="colgroup">Physics</th> </tr> <tr> <th scope="col">Practical</th> <th scope="col">Theory</th> <th scope="col">Practical</th> <th scope="col">Theory</th> <th scope="col">Practical</th> <th scope="col">Theory</th> </tr> <tr> <th scope="row">John Doe</th> <td>A</td> <td>A+</td> <td>B</td> <td>A</td> <td>A</td> <td>A-</td> </tr> <tr> <th scope="row">Miriam Luther</th> <td>A</td> <td>A</td> <td>C</td> <td>C+</td> <td>A</td> <td>A-</td> </tr> </table>
In the above markup we have added scope
to cells that contain heading information about the data cells.
Column Group
Biology, Chemistry and Physics cells are to be associated with a group of two columns each (Theory & Practical). Just adding colspan="2"
does not create the column groups, it only indicates that the particular cell is to occupy two cells’ worth of space.
To create columns group you must use colgroup
and then add the span
attribute to it indicating how many columns that column group includes.
Note: You can learn more about col
and colgroup
elements in this W3C documentation.
The <th rowspan="2" scope="col">Student Name</th>
markup with scope="col"
helps the assistive technology identify that the cells that follow in the same column are names of students.
Similarly, cells like <th colspan="2" scope="colgroup">Biology</th>
containing scope="colgroup"
helps users identify that the data in the cells that follow in the column group it spans over are associated with that particular subject.
Then there is the <th scope="row">John Doe</th>
markup with scope="row"
that defines that the cells following it in the same row, containing the “grade” information regarding that particular student name.
Row Groups
Now let us move onto another example, this example will have almost the same table as the one above except we’ll swap row and column headers, so we’ll be able to work with row groups.
<table> <tr> <th colspan="2">Subject</th> <th>John Doe</th> <th>Miriam Luther</th> </tr> <tr> <th rowspan="2">Biology</th> <th>Practical</th> <td>A</td> <td>A</td> </tr> <tr> <th>Theory</th> <td>A+</td> <td>A</td> </tr> <tr> <th rowspan="2">Chemistry</th> <th>Practical</th> <td>B</td> <td>C</td> </tr> <tr> <th>Theory</th> <td>A</td> <td>C+</td> </tr> <tr> <th rowspan="2">Physics</th> <th>Practical</th> <td>A</td> <td>A</td> </tr> <tr> <th>Theory</th> <td>A-</td> <td>A-</td> </tr> </table>
Now that we have our sample to work with let us start by creating row groups like we did for the column groups in the previous example.
However, row groups can not be created using a tag like colgroup
because there is no rowgroup
element.
HTML rows are generally grouped using <thead>
, <tbody>
and <tfooter>
elements. A single HTML <table>
element can have one <thead>
, one <tfoot>
and multiple <tbody>
. We’ll use multiple tbody
in our table to create the row groups, and add the respective scope to header cells.
<table> <colgroup span="2"></colgroup> <col> <col> <thead> <tr> <th colspan="2" scope="colgroup">Subject</th> <th scope="col">John Doe</th> <th scope="col">Miriam Luther</th> </tr> </thead> <tbody> <tr> <th rowspan="2" scope="rowgroup">Biology</th> <th>Practical</th> <td>A</td> <td>A</td> </tr> <tr> <th>Theory</th> <td>A+</td> <td>A</td> </tr> </tbody> <tbody> <tr> <th rowspan="2" scope="rowgroup">Chemistry</th> <th>Practical</th> <td>B</td> <td>C</td> </tr> <tr> <th>Theory</th> <td>A</td> <td>C+</td> </tr> </tbody> <tbody> <tr> <th rowspan="2" scope="rowgroup">Physics</th> <th>Practical</th> <td>A</td> <td>A</td> </tr> <tr> <th>Theory</th> <td>A-</td> <td>A-</td> </tr> </tbody> </table>
We have added the rows “Practical” and “Theory” in each tbody
creating row groups with two rows in each. We also have added the scope="rowgroup"
to the cells containing the heading information about those two rows (which is the subject name the grades belong to in this case).
Note: You can look into more about accessibility guidelines in the W3C site.