I need to create a html table (or something similar looking) with a fixed header and a fixed first column.
Every solution I've seen so far uses Javascript or jQuery
to set scrollTop/scrollLeft, but it doesn't work smoothly on mobile browsers, so I'm looking for a pure CSS solution.
I found a solution for a fixed column here: jsfiddle.net/C8Dtf/20/ but I don't know how I can enhance it to make the header fixed too.
I want it to work on webkit browsers or use some css3
features, but I repeat, I don't want to use Javascript
for scrolling.
EDIT: This is example of the behaviour I want to achieve: https://web.archive.org/web/20130829032141/http://datatables.net/release-datatables/extras/FixedColumns/css_size.html
By setting postion: sticky and top: 0, we can create a fixed header on a scroll in HTML tables.
The position: sticky
property supports both sticking to the top and to the side in modern versions of Chrome, Firefox, and Edge. This can be combined with a div
that has the overflow: scroll
property to give you a table with fixed headers that can be placed anywhere on your page.
Place your table in a container:
<div class="container">
<table></table>
</div>
Use overflow: scroll
on your container to enable scrolling:
div.container {
overflow: scroll;
}
As Dagmar pointed out in the comments, the container also requires a max-width
and a max-height
.
Use position: sticky
to have table cells stick to the edge and top
, right
, or left
to choose which edge to stick to:
thead th {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 0;
}
tbody th {
position: -webkit-sticky; /* for Safari */
position: sticky;
left: 0;
}
As MarredCheese mentioned in the comments, if your first column contains <td>
elements instead of <th>
elements, you can use tbody td:first-child
in your CSS instead of tbody th
To have the header in the first column stick to the left, use:
thead th:first-child {
left: 0;
z-index: 1;
}
/* Use overflow:scroll on your container to enable scrolling: */
div {
max-width: 400px;
max-height: 150px;
overflow: scroll;
}
/* Use position: sticky to have it stick to the edge
* and top, right, or left to choose which edge to stick to: */
thead th {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 0;
}
tbody th {
position: -webkit-sticky; /* for Safari */
position: sticky;
left: 0;
}
/* To have the header in the first column stick to the left: */
thead th:first-child {
left: 0;
z-index: 2;
}
/* Just to display it nicely: */
thead th {
background: #000;
color: #FFF;
/* Ensure this stays above the emulated border right in tbody th {}: */
z-index: 1;
}
tbody th {
background: #FFF;
border-right: 1px solid #CCC;
/* Browsers tend to drop borders on sticky elements, so we emulate the border-right using a box-shadow to ensure it stays: */
box-shadow: 1px 0 0 0 #ccc;
}
table {
border-collapse: collapse;
}
td,
th {
padding: 0.5em;
}
<div>
<table>
<thead>
<tr>
<th></th>
<th>headheadhead</th>
<th>headheadhead</th>
<th>headheadhead</th>
<th>headheadhead</th>
<th>headheadhead</th>
<th>headheadhead</th>
<th>headheadhead</th>
</tr>
</thead>
<tbody>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
<tr>
<th>head</th>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
<td>body</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/qwubvg9m/1/
Nowadays, this is possible to achieve using CSS only with position: sticky
property.
Here goes a snippet:
(jsFiddle: https://jsfiddle.net/hbqzdzdt/5/)
.grid-container {
display: grid; /* This is a (hacky) way to make the .grid element size to fit its content */
overflow: auto;
height: 300px;
width: 600px;
}
.grid {
display: flex;
flex-wrap: nowrap;
}
.grid-col {
width: 150px;
min-width: 150px;
}
.grid-item--header {
height: 100px;
min-height: 100px;
position: sticky;
position: -webkit-sticky;
background: white;
top: 0;
}
.grid-col--fixed-left {
position: sticky;
left: 0;
z-index: 9998;
background: white;
}
.grid-col--fixed-right {
position: sticky;
right: 0;
z-index: 9998;
background: white;
}
.grid-item {
height: 50px;
border: 1px solid gray;
}
<div class="grid-container">
<div class="grid">
<div class="grid-col grid-col--fixed-left">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
<div class="grid-item">
<p>Hello</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
<div class="grid-item">
<p>P</p>
</div>
</div>
<div class="grid-col grid-col--fixed-right">
<div class="grid-item grid-item--header">
<p>HEAD</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
<div class="grid-item">
<p>9</p>
</div>
</div>
</div>
</div>
Regarding compatibility. It works in all major browsers, but not in IE. There is a polyfill for position: sticky
but I never tried it.
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