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 1
<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:

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.

Table 2

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

Table 3

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.

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 4
<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).

WebsiteFacebookTwitterInstagramPinterestLinkedInGoogle+YoutubeRedditDribbbleBehanceGithubCodePenWhatsappEmail