A table with a fixed (sticky) header
Tags: html, css, webdev
Published: 2020-10-12 06:40:28

Often, we come across situations where we have a `table` with lots of data in it. So when we scroll, the table header are hidden off screen, and soon we don't know what columns we're looking at. This problem can easily be addressed by wrapping the `table` in a `div`, and a bit of friendly CSS.

## position: sticky
Sticky positioning is kind of a hybrid, between `relative` and `fixed`. To quote https://developer.mozilla.org/en-US/docs/Web/CSS/position, *"The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left."* Basically, the element occurs in the normal document flow, until you scroll past a certain offset, at which point it *sticks* to the top of the specified container.

We're going to wrap our `table` in a `div` (which we'll make scrollable by setting its `height` and `overflow-y` properties), and make the `th`s position in a sticky way. We'll also collapse the internal borders of the table using the `border-collapse` property. Is it really that simple? It actually is.

Here's the code, in its full unadulterated glory (don't worry, I didn't include any spoiler from season 2 of The Umbrella Academy, but seriously, go watch it):

### HTML
```html
<!DOCTYPE html>
<html>
  <head>
    <title>Sticky Header</title>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="src/styles.css" />
  </head>

  <body>
    <div class="wrapper">
      <table>
        <thead>
          <tr>
            <th>Number</th>
            <th>Name</th>
            <th>Superpower</th>
            <th>Email</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>Luther</td>
            <td>Super-strength</td>
            <td>luther@umbrella.edu</td>
          </tr>
          <tr>
            <td>2</td>
            <td>Diego</td>
            <td>Telekinesis</td>
            <td>diego@umbrella.edu</td>
          </tr>
          <tr>
            <td>3</td>
            <td>Allison</td>
            <td>Rumor</td>
            <td>allison@umbrella.edu</td>
          </tr>
          <tr>
            <td>4</td>
            <td>Klaus</td>
            <td>Seance</td>
            <td>klaus@umbrella.edu</td>
          </tr>
          <tr>
            <td>5</td>
            <td>Five</td>
            <td>Time-travel</td>
            <td>five@umbrella.edu</td>
          </tr>
          <tr>
            <td>6</td>
            <td>Ben</td>
            <td>Inter-dimensional monster</td>
            <td>ben@umbrella.edu</td>
          </tr>
          <tr>
            <td>7</td>
            <td>Vanya</td>
            <td>Apocalyptic destruction</td>
            <td>vanya@umbrella.edu</td>
          </tr>
          <tr>
            <td>-</td>
            <td>Reginald</td>
            <td>[Spoiler] powers</td>
            <td>reginald@umbrella.edu</td>
          </tr>
          <tr>
            <td>-</td>
            <td>Pogo</td>
            <td>Human communication</td>
            <td>pogo@umbrella.edu</td>
          </tr>
          <tr>
            <td>-</td>
            <td>Cha-Cha</td>
            <td>Ruthlessness</td>
            <td>chacha@temps.comm</td>
          </tr>
          <tr>
            <td>-</td>
            <td>Hazel</td>
            <td>Compassion</td>
            <td>hazel@temps.comm</td>
          </tr>
        </tbody>
      </table>
    </div>
  </body>
</html>
```

### CSS
```css
body {
  font-family: "Segoe UI", sans-serif;
}

table {
  width: 100%;
  border-collapse: collapse;
}

th,
td {
  padding: 8px;
}

th {
  text-align: left;
  background: #eee;
  position: sticky;
  top: 0px;
}

.wrapper {
  border: 1px solid #ddd;
  width: 100%;
  height: 300px;
  overflow-y: auto;
}
```

You can see the above example running on CodeSandbox here - https://codesandbox.io/s/determined-buck-396hm
{% codesandbox determined-buck-396hm %}

So long for now, and happy positioning!