It wasn’t long ago when I looked at sticky headers and footers in HTML <table>
s in the blog post A table with both a sticky header and a sticky first column. In it, I never used position: sticky
on any <thead>
, <tfoot>
, or <tr>
element, because even though Safari and Firefox could do that, Chrome could not. But it could do table cells like <th>
and <td>
, which was a decent-enough workaround.
Well that’s changed.
Sounds like a big effort went into totally revamping tables in the rendering engine in Chromium, bringing tables up to speed. It’s not just the stickiness that was fixed, but all sorts of things. I’ll just focus on the sticky thing since that’s what I looked at.
The headline to me is that <thead>
and <tfoot>
are sticky-able. That seems like it will be the most common use case here.
table thead,
table tfoot {
position: sticky;
}
table thead {
inset-block-start: 0; /* "top" */
}
table tfoot {
inset-block-end: 0; /* "bottom" */
}
That works in all three major browsers. You might want to get clever and only sticky them at certain minimum viewport heights or something, but the point is it works.
I heard several questions about table columns as well. My original article had a sticky first column (that was kind of the point). While there is a table <col>
tag, it’s… weird. It doesn’t actually wrap columns, it’s more like a pointer thing to be able to style down the column if you need to. I hardly ever see it used, but it’s there. Anyway, you totally can’t position: sticky;
a <col>
, but you can make sticky columns. You need to select all the cells in that column and stick them to the left or right. Here’s that using logical properties…
table tr th:first-child {
position: sticky;
inset-inline-start: 0; /* "left" */
}
Here’s a sorta obnoxious table where the <thead>
, <tfoot>
, and the first and last columns are all sticky.
I’m sure you could do something tasteful with this. Like maybe:
Excelent. Can the wikis pleeeease add that?
hey great article first of all, but can you tell me what did {font: 110%/1.4 system-ui} meant and how does browser read it.
The
110%
is referring tofont-size
The
1.4
is referring to theline-height
The
system-ui
is referring to thefont-family
Putting it all in one
font
declaration is called shorthand, and is completely valid! You can read more about it here on MDN.Doesn’t work on Microsoft Edge 91.0.864.48
Confirmed.
So what does that tell us? I think it tells us that Microsoft Edge, while it uses Chromium, doesn’t stay on-part release-to-release with Chrome. The “re-written” tables will likely land in a future release of Edge, I suppose.
Works now, at least.
Still doesn’t work for display: table-header-group :(
I have not tried this yet, just found it, but for those saying it does not work on Edge, try using
Edge-Dev
version. Edge now runs on the same “foundation” that Chrome and Firefox do, so it should work…I read your original article this morning to accomplish this, and it worked great.
Now it will be even more greater!
Thanks for this!
Doesn’t seem to do the trick on my mobile Chrome as well.
Unfortunately, it still seems nearly impossible to use both sticky header / footer rows and columns on most pages, because tables don’t respect parent widths. If your table is wider than its parent, it will run off the trailing edge (thereby preventing horizontal scrolling where sticky columns are useful) unless placed in a wrapper with
overflow-x: auto
(orscroll
). But if you do that, you’ll establish a new formatting context and lose the stickiness of the header / footer rows.See minimal example here: https://jsfiddle.net/jye05wzc/
I guess using an
iframe
as the wrapper is something of a workaround, but that’s quite a contortion for basic scrolling behavior.Actually, scratch what I said about
iframe
— I thought I’d seen one of the embedded examples correctly stick to the top edge of the article window (rather than its embedded window), but then I couldn’t find it again. Upon checking out the details, I don’t see any reason to thinkiframe
would confer any help here after all.That leaves us back at the start: do tables just really not support any kind of overflow scrolling where the table itself, and not a wrapper, is treated as the container for its cells?
I revisited this again, thinking I might have come across a solution in this CSS working group issue, and thought I ought to summarize.
overflow: clip
is a recently introduced value that allows its behavior to be applied on a per-axis basis. It doesn’t, on its own, establish a block formatting context, so I guessed thatoverflow-x: scroll
withoverflow-y: clip
might provide the desired behavior. Unfortunately neither Chrome nor Firefox implement it like this — as long asoverflow-x: scroll
is present, a new formatting context (which applies to both dimensions) is established regardless, and column headers won’t scroll with the page. It seems the working group issue 865 remains open specifically because of this use case, so I guess at this point the hope is forposition: sticky
to gain support for the desired behavior within parents that haveoverflow: hidden
oroverflow: scroll
.Hi Chris,
What would prevent a sticky header or footer being sticky for all table columns? Using the CSS you outlined, my header is sticky for all columns, however the footer is sticky for all except the last column? I’ve being searching for a solution including stackoverflow without success.
This works great, but when I apply it there’s a bit of extra underlying table both above and below any header/footer rows. This causes no functional problems, but is cosmetically ugly.
Not sure how to fix this, any ideas? Presumably the fix will also fix the small gap between the rows of the 2-row tfoot shown here:
https://staff.scheig.com/extraneous.png
I have Edge Version 93.0.961.38 and it’s working like Chrome.. Finally can be used across :)