Search code examples
htmlcsshtml-tablesticky

How to make a table head sticky but with overflow


I am trying to make the of a table sticky to top of page as you scroll, however the table width should be constrained by its parent and allow to scroll in the x direction.

However as soon as the overflow-x is added, the stickyness doesn't work, and if there's no overflow, then the head sticks, but the table content breaks the bounds of its parent container.

Example Pen https://codepen.io/JamesBlack91/pen/KKRJdQK

div {
  max-width: 300px;
  overflow-x: auto;
}

table thead {
  position: sticky;
  top: 0;
}
<div>
  <table>
    <thead>
      <tr>
        <th>Column 1</th>
        <th>Column 2</th>
        <th>Column 3</th>
        <th>Column 4</th>
        <th>Column 5</th>
        <th>Column 6</th>
        <th>Column 7</th>
        <th>Column 8</th>
        <th>Column 9</th>
        <th>Column 10</th>
        <th>Column 11</th>
        <th>Column 12</th>
        <th>Column 13</th>
        <th>Column 14</th>
        <th>Column 15</th>
        <th>Column 16</th>
        <th>Column 17</th>
        <th>Column 18</th>
        <th>Column 19</th>
        <th>Column 20</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
      <tr>
        <td>Some fake row</td>
      </tr>
    </tbody>
  </table>
</div>

If you scroll, the head isn't sticky, but if you comment out the overflow on the div, the head is sticky but the columns overflow the 300px max.

I have read all about thead (previously) not supporting sticky, I've also tried on the < th > to no result. I've tried putting overflow on the table/thead/tbody instead of the ancestor div, but nothing seems to work.

I really want to avoid using javascript and I'm sure there's a simple css solution I just don't know.


Solution

  • .lock-header {
      height: 100% !important;
    }
    
    .lock-header thead th {}
    
    .lock-header>table {}
    
    .lock-header tfoot td {}
    
    .lock-header {
      overflow-y: auto;
      max-height: 300px;
      width: 300px;
      padding: 0;
    }
    
    .lock-header thead>tr:first-child>th {
      background-color: rgba(240, 240, 240, 0.8);
      z-index: 1;
      position: sticky;
      top: 0;
    }
    
    .lock-header>table {
      border-collapse: collapse;
      width: 100%;
    }
    
    .lock-header tfoot td {
      background-color: rgba(240, 240, 240, 1);
      z-index: 99;
      position: sticky;
      bottom: 0;
    }
    <div class="lock-header">
      <table>
        <thead>
          <tr>
            <th>Column 1</th>
            <th>Column 2</th>
            <th>Column 3</th>
            <th>Column 4</th>
            <th>Column 5</th>
            <th>Column 6</th>
            <th>Column 7</th>
            <th>Column 8</th>
            <th>Column 9</th>
            <th>Column 10</th>
            <th>Column 11</th>
            <th>Column 12</th>
            <th>Column 13</th>
            <th>Column 14</th>
            <th>Column 15</th>
            <th>Column 16</th>
            <th>Column 17</th>
            <th>Column 18</th>
            <th>Column 19</th>
            <th>Column 20</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
          <tr>
            <td>Some fake row</td>
          </tr>
        </tbody>
      </table>
    </div>

    is this what you want ? I tried it on codepen and it works