6

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:

Community
  • 1
  • 1
Christopher Chiche
  • 15,075
  • 9
  • 59
  • 98

1 Answers1

7

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>
vkjgr
  • 4,338
  • 1
  • 26
  • 19
  • This solution looks great! Thanks! However it doesn't fix the border issue: http://codepen.io/anon/pen/ENmLBj . Do you have any pointer on this? So I'm not sure about what it adds compared to what I propose. Would you care to elaborate? – Christopher Chiche Nov 23 '16 at 10:43
  • Sweet. I would try `box-shadow: 0 0 0 1px gray`. Updated Pen http://codepen.io/vkjgr/pen/QGvmZv – vkjgr Nov 23 '16 at 11:04
  • Looks great! Last bit of info, is there a polyfill that works with this? From what I tried I don't think so :( – Christopher Chiche Nov 23 '16 at 12:44
  • I updated my pen with some issue for the column headers when there are multiple: http://codepen.io/anon/pen/PbmLQw I'll post my solution if I find it. – Christopher Chiche Nov 23 '16 at 17:17
  • 12
    The code snippet doesn't seem to work in the current version of chrome (58) – 1800 INFORMATION Jun 23 '17 at 02:46
  • You can wrap the CSS with `@-moz-document url-prefix() {thead th {box-shadow: 0 0 0 0.5px black}}` to make it target Firefox only, FYI. – Kernel James Dec 28 '22 at 04:51