Search code examples
csshtml-tablecss-positionstickyfixed-header-tables

Sticky row and column header in table


I am trying to design a table that has a sticky thead and also sticky row headers. So, basically, all th elements must be sticky.

I have stumbled across the position: sticky css3 attribute that seems to be a great candidate for the job, even though it's not yet supported in many browsers (which is not an issue to me). However the MDN documentation says:

The effect of ‘position: sticky’ on table elements is the same as for ‘position: relative’.

Getting this into consideration, I have built a basic example that works in Safari 10.0, even though the borders of the sticky elements are not conserved.

In firefox 50.0, the borders get don't get displayed but the headers are not sticky.

So, my question is: how can I cleanly achieve this fixed header positioning by using position: sticky. It seems like the implementation (when implemented) is not complete and tables are even less supported.

If it's not possible, I am also open to a solution in JavaScript that achieves this (but adding jQuery to my stack would be quite cumbersome since my whole app is in react).

Here is a code snippet of what I have for now. Please note that in order to have some sticky headers, you basically need safari or the alpha version of chrome.

div#container {
  height: 200px;
  width: 300px;
  overflow: auto;
}

table {
  border-collapse: collapse;
}

tbody th {
  position: -webkit-sticky;
  position: sticky;
  left: 0px;
}

thead {
  position: -webkit-sticky;
  position: sticky;
  top: 0px;
}

th {
  background: #B8C1C8;
  border: 2px solid black;
}
<div id="container">
  <table>
    <thead>
      <tr>
        <th>hehe</th>
        <th>hello</th>
        <th>world</th>
        <th>hello</th>
        <th>world</th>
        <th>hello</th>
        <th>world</th>
        <th>hello</th>
        <th>world</th>
        <th>hello</th>
        <th>world</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th>I'm a super long header</th>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
      </tr>
      <tr>
        <th>I'm a super long header</th>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
      </tr>
      <tr>
        <th>I'm a super long header</th>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
      </tr>
      <tr>
        <th>I'm a super long header</th>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
        <td>hello</td>
        <td>world</td>
      </tr>
    </tbody>
  </table>
</div>

Resources I have tried:


Solution

  • Seems likes you have everything there. It's also called as freezed panes effect. A bit tuned version:

    Forkable Codepen sample

    Update: better borders.

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    table {
      border-collapse: collapse;
      height: 20em;
      overflow: scroll;
      width: 50vw;
    }
    
    thead {
      background-color: #eee;
      color: gray;
      left: 0;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      z-index: 1;
    }
    thead th {
      background-color: #ddd;
    }
    thead th,
    thead td {
      box-shadow: 0 0 0 1px #ccc;
    }
    
    tr {
      border-bottom: thin solid #ddd;
      width: 100%;
    }
    
    th,
    td {
      min-width: 20em;
      padding: 0.5em;
    }
    
    th {
      background-color: #eee;
      box-shadow: 1px 0 0 0 #ccc;
      left: 0;
      min-width: 5em;
      position: -webkit-sticky;
      position: sticky;
    }
    <table>
      <thead>
        <tr>
          <th></th>
          <td>
            A
          </td>
          <td>
            B
          </td>
          <td>
            C
          </td>
          <td>
            D
          </td>
        </tr>
      </thead>
      <tbody></tbody>
      <tr>
        <th>
          1
        </th>
        <td>
          A 1
        </td>
        <td>
          B 1
        </td>
        <td>
          C 1
        </td>
        <td>
          D 1
        </td>
      </tr>
      <tr>
        <th>
          2
        </th>
        <td>
          A 2
        </td>
        <td>
          B 2
        </td>
        <td>
          C 2
        </td>
        <td>
          D 2
        </td>
      </tr>
      <tr>
        <th>
          3
        </th>
        <td>
          A 3
        </td>
        <td>
          B 3
        </td>
        <td>
          C 3
        </td>
        <td>
          D 3
        </td>
      </tr>
      <tr>
        <th>
          4
        </th>
        <td>
          A 4
        </td>
        <td>
          B 4
        </td>
        <td>
          C 4
        </td>
        <td>
          D 4
        </td>
      </tr>
      <tr>
        <th>
          5
        </th>
        <td>
          A 5
        </td>
        <td>
          B 5
        </td>
        <td>
          C 5
        </td>
        <td>
          D 5
        </td>
      </tr>
      <tr>
        <th>
          6
        </th>
        <td>
          A 6
        </td>
        <td>
          B 6
        </td>
        <td>
          C 6
        </td>
        <td>
          D 6
        </td>
      </tr>
      <tr>
        <th>
          7
        </th>
        <td>
          A 7
        </td>
        <td>
          B 7
        </td>
        <td>
          C 7
        </td>
        <td>
          D 7
        </td>
      </tr>
      <tr>
        <th>
          8
        </th>
        <td>
          A 8
        </td>
        <td>
          B 8
        </td>
        <td>
          C 8
        </td>
        <td>
          D 8
        </td>
      </tr>
      <tr>
        <th>
          9
        </th>
        <td>
          A 9
        </td>
        <td>
          B 9
        </td>
        <td>
          C 9
        </td>
        <td>
          D 9
        </td>
      </tr>
      <tr>
        <th>
          10
        </th>
        <td>
          A 10
        </td>
        <td>
          B 10
        </td>
        <td>
          C 10
        </td>
        <td>
          D 10
        </td>
      </tr>
      <tr>
        <th>
          11
        </th>
        <td>
          A 11
        </td>
        <td>
          B 11
        </td>
        <td>
          C 11
        </td>
        <td>
          D 11
        </td>
      </tr>
      <tr>
        <th>
          12
        </th>
        <td>
          A 12
        </td>
        <td>
          B 12
        </td>
        <td>
          C 12
        </td>
        <td>
          D 12
        </td>
      </tr>
      <tr>
        <th>
          13
        </th>
        <td>
          A 13
        </td>
        <td>
          B 13
        </td>
        <td>
          C 13
        </td>
        <td>
          D 13
        </td>
      </tr>
      <tr>
        <th>
          14
        </th>
        <td>
          A 14
        </td>
        <td>
          B 14
        </td>
        <td>
          C 14
        </td>
        <td>
          D 14
        </td>
      </tr>
      <tr>
        <th>
          15
        </th>
        <td>
          A 15
        </td>
        <td>
          B 15
        </td>
        <td>
          C 15
        </td>
        <td>
          D 15
        </td>
      </tr>
      <tr>
        <th>
          16
        </th>
        <td>
          A 16
        </td>
        <td>
          B 16
        </td>
        <td>
          C 16
        </td>
        <td>
          D 16
        </td>
      </tr>
      <tr>
        <th>
          17
        </th>
        <td>
          A 17
        </td>
        <td>
          B 17
        </td>
        <td>
          C 17
        </td>
        <td>
          D 17
        </td>
      </tr>
      <tr>
        <th>
          18
        </th>
        <td>
          A 18
        </td>
        <td>
          B 18
        </td>
        <td>
          C 18
        </td>
        <td>
          D 18
        </td>
      </tr>
      <tr>
        <th>
          19
        </th>
        <td>
          A 19
        </td>
        <td>
          B 19
        </td>
        <td>
          C 19
        </td>
        <td>
          D 19
        </td>
      </tr>
      <tr>
        <th>
          20
        </th>
        <td>
          A 20
        </td>
        <td>
          B 20
        </td>
        <td>
          C 20
        </td>
        <td>
          D 20
        </td>
      </tr>
    </table>