htmlinternet-explorercss

CSS-Only Scrollable Table with fixed headers


I have a solution by which I can create scrollable tables w/fixed header/footer using minor jQuery and CSS - but I am looking for a way to make this a CSS-only solution that is cross-browser compliant.

To be clear, what I am seeking to do is use only a table tag (and it's valid sub-tags, colgroup, col, thead, tbody, tfoot, tr, th, td), but adopt a set of CSS rules which will meet the following conditions:

  1. Must maintain column alignment between header / footer / content rows
  2. Must allow the header/footer to remain fixed while the content scrolls vertically
  3. Must not require any jQuery or other JavaScript in order to provide the functionality
  4. Must only use the tags provided above

This code example: http://jsfiddle.net/TroyAlford/SNKfd/ shows my current approach. Most of the JS is just to populate the table with random values, but the last portion is what drives the left/right scrollability.

$tbody.bind('scroll', function(ev) {
    var $css = { 'left': -ev.target.scrollLeft };
    $thead.css($css);
    $tfoot.css($css);
});

NOTE: The example provided does not render properly in IE, and requires jQuery to provide the horizontal scrolling. I don't care about horizontal scrolling anyway, so it's fine if a solution doesn't do that.


Solution

  • Check support of position: sticky before using this solution: https://caniuse.com/#feat=css-sticky


    Use of position: sticky

    An alternative answer would be using position: sticky. As described by W3C:

    A stickily positioned box is positioned similarly to a relatively positioned box, but the offset is computed with reference to the nearest ancestor with a scrolling box, or the viewport if no ancestor has a scrolling box.

    This described exactly the behavior of a relative static header. It would be easy to assign this to the <thead> or the first <tr> HTML-tag, as this should be supported according to W3C. However, both Chrome, IE and Edge have problems assigning a sticky position property to these tags. There also seems to be no priority in solving this at the moment.

    What does seem to work for a table element is assigning the sticky property to a table-cell. In this case the <th> cells.

    Because a table is not a block-element that respects the static size you assign to it, it is best to use a wrapper element to define the scroll-overflow.

    The code

    div {
      display: inline-block;
      height: 150px;
      overflow: auto
    }
    
    table th {
      position: -webkit-sticky;
      position: sticky;
      top: 0;
    }
    
    
    /* == Just general styling, not relevant :) == */
    
    table {
      border-collapse: collapse;
    }
    
    th {
      background-color: #1976D2;
      color: #fff;
    }
    
    th,
    td {
      padding: 1em .5em;
    }
    
    table tr {
      color: #212121;
    }
    
    table tr:nth-child(odd) {
      background-color: #BBDEFB;
    }
    <div>
      <table border="0">
        <thead>
          <tr>
            <th>head1</th>
            <th>head2</th>
            <th>head3</th>
            <th>head4</th>
          </tr>
        </thead>
        <tr>
          <td>row 1, cell 1</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
        </tr>
        <tr>
          <td>row 2, cell 1</td>
          <td>row 2, cell 2</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
        </tr>
        <tr>
          <td>row 2, cell 1</td>
          <td>row 2, cell 2</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
        </tr>
        <tr>
          <td>row 2, cell 1</td>
          <td>row 2, cell 2</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
        </tr>
        <tr>
          <td>row 2, cell 1</td>
          <td>row 2, cell 2</td>
          <td>row 1, cell 2</td>
          <td>row 1, cell 2</td>
        </tr>
      </table>
    </div>

    In this example I use a simple <div> wrapper to define the scroll-overflow done with a static height of 150px. This can of course be any size. Now that the scrolling box has been defined, the sticky <th> elements will corespondent "to the nearest ancestor with a scrolling box", which is the div-wrapper.


    ###Use of a `position: sticky` *polyfill* Non-supported devices can make use of a polyfill, which implements the behavior through code. An example is [stickybits][5], which resembles the same behavior as the browser's implemented `position: sticky`.

    Example with polyfill: http://jsfiddle.net/7UZA4/6957/