70

I have the following Sass snippet in which I want the <thead> to float as the table scrolls. This works properly in Safari, but not in Chrome Version 58.0.3029.110 (64-bit).

I know that Chrome has had on-again-off-again support for sticky, and currently supports it, but is it final? Is this a Chrome bug or do I need a different solution? (I prefer the CSS approach rather than Javascript because it's more performant.)

.table {
  thead {
    background: white;
    position: sticky;
    top: 0;
    z-index: 10;
  }
}
Donnie
  • 6,229
  • 8
  • 35
  • 53
  • 4
    Based on browser support, I'd just use fixed instead of sticky. To my knowledge support isn't 100% yet. – Michael May 16 '17 at 12:56
  • 2
    Related Chromium Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=702927 – styfle Apr 05 '18 at 15:36

4 Answers4

86

position: sticky doesn't work with some table elements (thead/tr) in Chrome. You can move sticky to tds/ths of tr you need to be sticky. Like this:

.table {
  thead tr:nth-child(1) th{
    background: white;
    position: sticky;
    top: 0;
    z-index: 10;
  }
}

Also this will work.

.table {
    position: sticky;
    top: 0;
    z-index: 10;
}

You can move header to separate layout. For example:

<table class="table">
    <thead>
    <tr>
        <th>1</th>
        <th>2</th>
        <th>3</th>
        <th>4</th>
    </tr>
    </thead>
</table>
<table>
    <tbody>
    <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
    <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
    </tr>
</table>
Kirill Matrosov
  • 5,564
  • 4
  • 28
  • 39
  • 22
    the separate layout option is only viable if the columns are all the same width – Rick Viscomi Sep 12 '17 at 23:36
  • 62
    Worth noting that this won't work if the table is contained inside an element with `overflow: auto` or `overflow-x: auto`. This occurs if you wrap a table in bootstrap's `.table-responsive` class. – Chris Barr Feb 01 '18 at 19:35
  • 2
    I agree with Rick Viscomi about the separate layout option is only viable if the columns are all the same width and also that solution is not without drawbacks if after the table there is other content because the sticky table for the whole content even after the end of the second table with the content, so it is unusable in most situations. It is possible to see an example on jfiddle at the following address: https://jsfiddle.net/eg6s4097/1/ with all mentioned drawbacks – willy wonka May 13 '18 at 06:29
  • Well how do you deal with an issue where border stays in place? – baldrs Apr 17 '19 at 21:43
  • 1
    @CBarr I am running into this exact issue. Do you have any solutions if you need to wrap it in a table-responsive class? – misteroptimist Jan 22 '20 at 21:48
  • @misteroptimist *That's* the big problem. You may find a suitable solution here: https://uxdesign.cc/position-stuck-96c9f55d9526... but then again, maybe not. – isacvale Mar 02 '20 at 17:00
  • You can add `position: sticky` directly to `thead` in Chrome now. – Leponzo Aug 31 '22 at 04:22
43

For somebody, who is still looking for a solution and isn't satisfied with the accepted one.

1) Use sticky-top class on th element.

2) With own class

th.sticky-header {
  position: sticky;
  top: 0;
  z-index: 10;
  /*To not have transparent background.
  background-color: white;*/
}
<table class="table">
  <thead>
    <tr>
      <th class="sticky-header">1</th>
      <th class="sticky-header">2</th>
      <th class="sticky-header">3</th>
      <th class="sticky-header">4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr>
  </tbody>
</table>
SuperDJ
  • 7,488
  • 11
  • 40
  • 74
Tomsgu
  • 1,016
  • 1
  • 11
  • 31
  • 5
    What about if there are two (or more) header rows in `thead`? – Jivan Apr 02 '19 at 01:57
  • @Jivan add a class on second thead with 'top' property set to the height of the first header. This is not dynamic, you have to know the height of your first thead. – Jon Nov 15 '19 at 14:14
  • 1
    Thank you! As Cbarr said: "Worth noting that this won't work if the table is contained inside an element with overflow: auto or overflow-x: auto. This occurs if you wrap a table in bootstrap's .table-responsive class." this was my case and your answer Tomsgu solved this! – Dimitri Dec 21 '19 at 21:18
8

You set the sticky position on the header table-cell instead of table-row.

.td.header {
  position: sticky;
  top:0px;
}

Check out this jsfiddle for simple example.

CarCar
  • 680
  • 4
  • 13
2

https://jsfiddle.net/y9cwnb81/4/

div.table {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr auto;

  grid-template-areas:
    "header header header"
    "content content content";
}

.header {
  grid: 1fr/1fr;
}

.content {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: auto;
  grid-area: content;
  height: 200px;
  overflow-y: scroll;
}

.content div {
  grid: auto-flow 1fr / repeat(auto-fill);
}

<!-- Here is the table code -->

<div class="table">
  <div class="header">Name</div>
  <div class="header">Color</div>
  <div class="header">Description</div>
  <div class="content">
    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd,mas, da,msnd asndm,asndm,asndbansbdansmbdmnasbd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>

    <div>Apple</div>
    <div>Red</div>
    <div>These are red asd.</div>
  </div>
</div>

Here is an example using CSS GRID to have sticky headers only with CSS, no javascript required.

  • This is a grid with a fixed height and "overflow-y: scroll". It's not using "position: sticky" (so the headers don't "stick" to the top of the viewport). Nevertheless, it's a useful answer that could be adapted to use "position: sticky". Thanks! – Patrick McElhaney Mar 30 '18 at 13:23
  • 1
    This doesnt work as intended since the header is also fixed in the horizontal direction – nights May 20 '19 at 03:45