I have a table that is grouped with multiple <tbody>
elements, each one having one initial row with a <th>
element that titles the group. I wan't these <th>
:s to be sticky but I can't get safari to set the correct top position.
If I set top: 0px
or any positive value on the <th>
:s it works as expected in Firefox, Chrome and Edge but in safari I get 0px + the height of all above tbodies and caption. This results in all <th>
:s getting stuck at the same time well below the top scroll position.
I can work around the problem by flattening the table, removing the <tbody>
elements and putting all <tr>
:s directly under the table. But that would complicate styling and it makes sense for accessibility to group the table in my case so I'd prefer if I didn't have to do that.
Do you have any ideas how can fix or work around this problem?
Expected (Chrome, Safari and Edge):
Safari 12:
div {
height: 200px;
overflow: auto;
}
td, th {
border: 1px solid gray;
}
th {
position: -webkit-sticky;
position: sticky;
top: 0;
background: white;
}
tbody th {
top: 20px;
}
td {
background: lightgray;
}
<div>
<table>
<caption>The table</caption>
<thead>
<tr>
<th>First col</th>
<th>Second col</th>
<th>Third col</th>
</tr>
</thead>
<tbody>
<tr>
<th colspan="3">Group 1</th>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
<tbody>
<tr>
<th colspan="3">Group 2</th>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
<tbody>
<tr>
<th colspan="3">Group 3</th>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
</table>
</div>
The code below will work for both Safari and Google Chrome. The trick is to change your th
's to thead
's and then set the position to sticky in various ways. position: -webkit-ms-sticky
, position: -webkit-o-sticky
, position: -webkit-sticky
, position:sticky
.
You should also add two check in css for the thead
and in the second check find every child of that thead
tag to ensure it selects each child tag in Chrome as well.
div {
height: 200px;
overflow: auto;
}
td, th {
border: 1px solid gray;
}
tbody thead {
top: 20px;
}
thead {
position: -webkit-sticky;
position: -webkit-o-sticky;
position: -webkit-ms-sticky;
position: sticky;
z-index: 1;
top: 0px;
background: white;
}
thead tr:nth-child(1) th{
background: white;
position: sticky;
top: 0;
z-index: 10;
}
td {
background: lightgray;
}
<table>
<caption>The table</caption>
<thead>
<tr>
<th>First col</th>
<th>Second col</th>
<th>Third col</th>
</tr>
</thead>
<tbody>
<thead>
<tr>
<th colspan="3">Group 1</th>
</tr>
</thead>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
<tbody>
<thead>
<tr>
<th colspan="3">Group 2</th>
</tr>
</thead>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
<tbody>
<thead>
<tr>
<th colspan="3">Group 3</th>
</tr>
</thead>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
<td>baz</td>
</tr>
</tbody>
</table>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With