Table fixed header and scrollable body

Fixed table head – CSS-only

Simply position: sticky; top: 0; your th elements. (Chrome, FF, Edge)

.tableFixHead          { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }

/* Just common table stuff. Really. */
table  { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th     { background:#eee; }
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><td>A1</td><td>A2</td></tr>
      <tr><td>B1</td><td>B2</td></tr>
      <tr><td>C1</td><td>C2</td></tr>
      <tr><td>D1</td><td>D2</td></tr>
      <tr><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>

For both sticky vertical TH and horizontal TH columns (inside TBODY):

.tableFixHead          { overflow: auto; height: 100px; width: 240px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead tbody th { position: sticky; left: 0; }

TH borders problem fix

Since border cannot be painted properly on a translated TH element, to recreate and render “borders” use the box-shadow property:

/* Borders (if you need them) */
.tableFixHead,
.tableFixHead td {
  box-shadow: inset 1px -1px #000;
}
.tableFixHead th {
  box-shadow: inset 1px 1px #000, 0 1px #000;
}

Fixed table head – using JS. (IE)

You can use a bit of JS and translateY the th elements

jQuery example

var $th = $('.tableFixHead').find('thead th')
$('.tableFixHead').on('scroll', function() {
  $th.css('transform', 'translateY('+ this.scrollTop +'px)');
});
.tableFixHead { overflow-y: auto; height: 100px; }

/* Just common table stuff. */
table  { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th     { background:#eee; }
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><td>A1</td><td>A2</td></tr>
      <tr><td>B1</td><td>B2</td></tr>
      <tr><td>C1</td><td>C2</td></tr>
      <tr><td>D1</td><td>D2</td></tr>
      <tr><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

Or plain ES6 if you prefer (no jQuery required):

// Fix table head
function tableFixHead (e) {
    const el = e.target,
          sT = el.scrollTop;
    el.querySelectorAll("thead th").forEach(th => 
      th.style.transform = `translateY(${sT}px)`
    );
}
document.querySelectorAll(".tableFixHead").forEach(el => 
    el.addEventListener("scroll", tableFixHead)
);

Leave a Comment