There ain't no web developer that never worried about a table with a fixed header. HTML browsers should implement this out of the box. But they don't. We need to do it.
Here is a normal HTML table with a height restricted to 12 em.
One | Two | Three |
---|---|---|
1 Apples | Oranges | Plum |
2 Apples | Oranges | Plum |
3 Apples | Oranges | Plum |
4 Apples | Oranges | Plum |
5 Apples | Oranges | Plum |
6 Apples | Oranges | Plum |
7 Apples | Oranges | Plum |
8 Apples | Oranges | Plum |
9 Apples | Oranges | Plum |
10 Apples | Oranges | Plum |
11 Apples | Oranges | Plum |
12 Apples | Oranges | Plum |
13 Apples | Oranges | Plum |
14 Apples | Oranges | Plum |
15 Apples | Oranges | Plum |
16 Apples | Oranges | Plum |
17 Apples | Oranges | Plum |
18 Apples | Oranges | Plum |
19 Apples | Oranges | Plum |
20 Apples | Oranges | Plum |
Here is the same with fixed header.
One | Two | Three |
---|---|---|
1 Apples | Oranges | Plum |
2 Apples | Oranges | Plum |
3 Apples | Oranges | Plum |
4 Apples | Oranges | Plum |
5 Apples | Oranges | Plum |
6 Apples | Oranges | Plum |
7 Apples | Oranges | Plum |
8 Apples | Oranges | Plum |
9 Apples | Oranges | Plum |
10 Apples | Oranges | Plum |
11 Apples | Oranges | Plum |
12 Apples | Oranges | Plum |
13 Apples | Oranges | Plum |
14 Apples | Oranges | Plum |
15 Apples | Oranges | Plum |
16 Apples | Oranges | Plum |
17 Apples | Oranges | Plum |
18 Apples | Oranges | Plum |
19 Apples | Oranges | Plum |
20 Apples | Oranges | Plum |
Don't see no difference? It's not always the look, sometimes it's the feel :-)
You feel the difference as soon as you scroll down. The fixed header stays at its position while the table content scrolls under it. The non-fixed header scrolls away with the content and is no more visible.
How to achieve this? We now enter the kingdom of absolute and relative positioning :-)
When you explored my latest Blog you can skip this chapter.
An HTML element that was positioned by CSS position: relative
(or absolute
, or fixed
) is the point of reference for its children with position: absolute
.
An HTML element that was positioned by CSS position: absolute
will position itself relative to its nearest parent with a position
different to static
. The relative position is then given by top, left, right, bottom
coordinates.
How can this knowledge be applied to fix a table header?
We need ...
div
with position: relative
that will be the reference point for the absolutely positioned header div
to restrict the table-content to a certain height
, and restrict the content's overflow
to auto
, else it won't have a scrollbar (default overflow is visible) position: absolute
and give it a top: 0
coordinate (default coordinate would be its static position below or beside its predecessor HTML element) Sounds simple and logical. The header would then go absolutely to its relative parent and stay there, while the table scrolls under it.
Unfortunately the browser's table implementations make it impossible to tear the header cells out of the table and pull it up onto some siding.
And, fortunately, clever people have found a workaround for that:
th
header cells into div
elements, anddiv
absolutely (instead of the th
cells).So, step by step now.
.headerSiding {
position: relative; /* need a non-static position */
padding-top: 1.4em; /* place for the fixed header */
}
The topmost parent is positioned relatively, to be the reference point for absolutely positioned child elements. It prepares a siding for the header by declaring a padding. (This padding might need adjustment for headers that are multi-line.)
<div class="headerSiding">
....
</div>
.scrollPane {
height: 20em; /* without height no scrollbar ever */
overflow: auto; /* show scrollbar when needed */
}
This is a standard scroll pane. By setting the height
, and setting overflow: auto
, you force scrollbars when the content does not fit into the height. Mind that this is positioned static, NOT relative, else the scrollPane would be the reference point for the table header, not the headerSiding!
<div class="headerSiding">
<div class="scrollPane">
....
</div>
</div>
.scrollPane th div {
position: absolute; /* pinned to next non-static parent */
top: 0; /* at top of parent */
}
This is the tricky part, nevertheless very short. Simply tell the div
elements containing the header cell content to go to top, where a padding was prepared for them. That's all concerning CSS, now we also need to look at the HTML.
<div class="headerSiding">
<div class="scrollPane">
<table>
<thead>
<tr>
<th><div>One</div></th><th><div>Two</div></th><th><div>Three</div></th>
</tr>
</thead>
<tbody>
<tr>
<td>1 Apples</td><td>Oranges</td><td>Plum</td>
</tr>
<tr>
<td>2 Apples</td><td>Oranges</td><td>Plum</td>
</tr>
....
<tbody>
</table>
</div>
</div>
This shows the wrapping of the th
header cell contents into div
elements.
No more tricks needed for this. This is the layout part. One of the few pure CSS soutions I can believe in. I also tried this out on complex pages with further relative and absolute parents, it works everywhere.
You can view this example also on my hompage.
Click here to see full source code.
1 | <!DOCTYPE HTML> |
ɔ⃝ Fritz Ritzberger, 2015-07-20