On many web projects, we need to display tabular data. We don't need a fancy library to deal with tables most of the time. When the amount of data is small, features such as virtualization are over-skilled. Regular HTML tables can do the job. But it does not mean that we cannot add handy features such as a fixed first column.
Let's start with a basic HTML table.
<table>
<thead>
<tr>
<th>DATA1</th>
<th>DATA2</th>
<th>DATA3</th>
<th>DATA4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Some values</td>
<td>Some values</td>
<td>Some values</td>
<td>Some values</td>
</tr>
<tr>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
</tr>
<tr>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
</tr>
<tr>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
<td>Other values</td>
</tr>
</tbody>
</table>
With some styling, we end up with the following UI.
When horizontal scrolling is required, we want a fixed first column. Let's see how to implement this feature in CSS.
First, we need to use overflow-x:auto to make the scrollbar appears when necessary.
Sadly, setting this property directly to the table element seems to have no effect.
According to the CSS specifications, overflow properties only apply to block, flex, and grid containers. We could change the display property of the table. But, semantically, it's a table, not a block. Therefore, I prefer to use a wrapper element.
<div class="container">
<table>
<thead>
<tr>
<th>DATA1</th>
<th>DATA2</th>
<th>DATA3</th>
<th>DATA4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Some values</td>
<td>Some values</td>
<td>Some values</td>
<td>Some values</td>
</tr>
</tbody>
</table>
</div>
.container {
overflow-x: auto;
}
When we scroll horizontally, the first column should "stick" the left edge of the table. This is where sticky positioning comes to into play.
The idea about sticky positioning is that as you scroll, an element can "stick" to the edge.
Here's what the CSS code would be like:
tr>th:first-child,tr>td:first-child {
position: sticky;
left: 0;
}
The tr>th:first-child,tr>td:first-child selector only applies sticky positioning to the first column cells.
This solution seems pretty good. But, not so fast! With that, you'll get the following side effect:
The first column is sticky (and it works), but we can still see the content of other columns (under our first column) while we scroll.
To understand this issue, let's have a look at our background definition:
tr:nth-child(odd) {
background: $white;
}
tr:nth-child(even) {
background: $gray-200;
}
The background property is defined at the row level. It means that the cell has no background. However, we want the first column cells to have a background. So it hides other cells when we scroll.
We end up with this CSS:
tr:nth-child(odd) td {
background: $white;
}
tr:nth-child(even) td {
background: $gray-200;
}
So here's the final version of the CSS:
.container {
overflow-x: auto;
}
tr>th:first-child,tr>td:first-child {
position: sticky;
left: 0;
}
tr:nth-child(odd) td {
background: $white;
}
tr:nth-child(even) td {
background: $gray-200;
}
And really that's it.
You can find the source code here.
Top comments (4)
Good one! :) Also added
table { table-layout: fixed }
to get equal width columns.this was super helpful thank you! easy to adapt to fake tables also
👍
Great!! Very helpful and easy to implement :)