5

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.

3 Answers 3

5

This is elegant AND it works:

<style>
 
.lock-header { 
  overflow-y: auto; 
  max-height: 500px;
}  
.lock-header thead { 
  position: sticky; 
  top: 0; 
}

</style>

<div class="lock-header">
    <table>
        <thead>
             <tr>
                 <th>Column head</th>
             </tr>

        </thead>
        <tbody>
             <tr>
                 <td>Detail row 1</th>
             </tr>
             <tr>
                 <td>Detail row 2</th>
             </tr>
        </tbody>
    </table>
</div>
Sign up to request clarification or add additional context in comments.

Comments

0

.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

3 Comments

This is perfect, thank you! It seems the trick was to add the max-height to the wrapping div, in my case I need it full screen so just changed to 100vh and it's perfect, thank you!
100vh feels a bit bad since it does not allow the table to grow longer than the height of the viewport.
I don't like double scroll bars on the same page.
0

This can't be done with just CSS. The sticky position requires the initial overflow value on parent containers, you will have to use JS to stick the header row.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.