Creating an accessible table with a sticky header can be accomplished in a couple of different ways. The way not to accomplish this is to separate the table into two different tables, one with the headers and one with the data cells. That breaks the header relationships and creates an inaccessible table. Instead, consider the following table:
Col 1 | Col 2 | Col 3 | Col 4 |
---|---|---|---|
Row Header 1 | Data 1 | Data 2 | Data 3 |
Row Header 2 | Data 1 | Data 2 | Data 3 |
Row Header 3 | Data 1 | Data 2 | Data 3 |
Row Header 4 | Data 1 | Data 2 | Data 3 |
Row Header 5 | Data 1 | Data 2 | Data 3 |
Row Header 6 | Data 1 | Data 2 | Data 3 |
Row Header 7 | Data 1 | Data 2 | Data 3 |
Row Header 8 | Data 1 | Data 2 | Data 3 |
Row Header 9 | Data 1 | Data 2 | Data 3 |
Row Header 10 | Data 1 | Data 2 | Data 3 |
Row Header 11 | Data 1 | Data 2 | Data 3 |
Row Header 12 | Data 1 | Data 2 | Data 3 |
Row Header 13 | Data 1 | Data 2 | Data 3 |
Row Header 14 | Data 1 | Data 2 | Data 3 |
Row Header 15 | Data 1 | Data 2 | Data 3 |
Row Header 16 | Data 1 | Data 2 | Data 3 |
Row Header 17 | Data 1 | Data 2 | Data 3 |
Row Header 18 | Data 1 | Data 2 | Data 3 |
Row Header 19 | Data 1 | Data 2 | Data 3 |
Row Header 20 | Data 1 | Data 2 | Data 3 |
Row Header 21 | Data 1 | Data 2 | Data 3 |
Row Header 22 | Data 1 | Data 2 | Data 3 |
Row Header 23 | Data 1 | Data 2 | Data 3 |
Row Header 24 | Data 1 | Data 2 | Data 3 |
Code
In short, the best way to accomplish this task is to fix the THEAD
with a position: sticky
and the rest of the table with overflow. The table itself is built normally with two exceptions. First there is a wrapper around the table with a CSS class on it (which we will define next), and then there is a tabindex="0"
on the table itself, which is necessary for support using the Chrome web browser.
<div class="fixTableHead">
<table tabindex="0">
<caption class="sr-only">CSS Table with sticky header</caption>
<thead>
<tr>
<th scope="col">Col 1</th>
<th scope="col">Col 2</th>
<th scope="col">Col 3</th>
<th scope="col">Col 4</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Row Header 1</th>
<td>Data 1</td>
<td>Data 2</td>
<td>Data 3</td>
</tr>
<!-- Insert More Rows Here -->
</tbody>
</table>
</div>
Then the CSS itself is pretty simple with just three declarations. The first for the wrapper defines how the region will overflow, the second then tells the thead row to be stuck in place. Finally the last declaration, adds visual focus to the table for the chrome users.
.fixTableHead {
overflow-y: auto;
height: 15em;
}
.fixTableHead thead th {
position: sticky;
top: 0;
}
table:focus {
border: #f00 solid 2px !important;
}
There are additional variations on how to create a sticky table header, but this one is the simplest that we have seen.