[html] 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.

This question is related to html internet-explorer css

The answer is


I had the same problem and after spending 2 days researching I found this solution from Ksesocss that fits for me and maybe is good for you too. It allows fixed header and dynamic width and only uses CSS. The only problem is that the source is in spanish but you can find the html and css code there.

This is the link:

http://ksesocss.blogspot.com/2014/10/responsive-table-encabezado-fijo-scroll.html

I hope this helps


Ive achieved this easily using this code :

So you have a structure like this :

<table>
<thead><tr></tr></thead>
<tbody><tr></tr></tbody>
</table>

just style the thead with :

<style>
thead{ 
    position: -webkit-sticky;
    position: -moz-sticky;
    position: -ms-sticky;
    position: -o-sticky;
    position: sticky;
    top: 0px;
}
</style>

Three things to consider :

First, this property is new. It’s not supported at all, apart from the beta builds of Webkit-based browsers. So caveat formator. Again, if you really want for your users to benefit from sticky headers, go with a javascript implementation.

Second, if you do use it, you’ll need to incorporate vendor prefixes. Perhaps position: sticky will work one day. For now, though, you need to use position:-webkit-sticky (and the others; check the block of css further up in this post).

Third, there aren’t any positioning defaults at the moment, so you need to at least include top: 0; in the same css declaration as the position:-webkit-sticky. Otherwise, it’ll just scroll off-screen.


If you have the option of giving a fixed width to the table cells (and a fixed height to the header), you can used the position: fixed option:

http://jsfiddle.net/thundercracker/ZxPeh/23/

You would just have to stick it in an iframe. You could also have horizontal scrolling by giving the iframe a scrollbar (I think).


Edit 2015

If you can live with a pre-defining the width of your table cells (by percentage), then here's a bit more elegant (CSS-only) solution:

http://jsfiddle.net/7UBMD/77/


Only with CSS :

CSS:

tr {
  width: 100%;
  display: inline-table;
  table-layout: fixed;
}

table{
 height:300px;              // <-- Select the height of the table
 display: -moz-groupbox;    // Firefox Bad Effect
}
tbody{
  overflow-y: scroll;      
  height: 200px;            //  <-- Select the height of the body
  width: 100%;
  position: absolute;
}

Bootply : http://www.bootply.com/AgI8LpDugl


As I was recently in need of this, I will share a solution that uses 3 tables, but does not require JavaScript.

Table 1 (parent) contains two rows. The first row contains table 2 (child 1) for the column headers. The second row contains table 3 (child 2) for the scrolling content.

It must be noted the childTbl must be 25px shorter than the parentTbl for the scroller to appear properly.

This is the source, where I got the idea from. I made it HTML5-friendly without the deprecated tags and the inline CSS.

_x000D_
_x000D_
.parentTbl table {_x000D_
  border-spacing: 0;_x000D_
  border-collapse: collapse;_x000D_
  border: 0;_x000D_
  width: 690px;_x000D_
}_x000D_
.childTbl table {_x000D_
  border-spacing: 0;_x000D_
  border-collapse: collapse;_x000D_
  border: 1px solid #d7d7d7;_x000D_
  width: 665px;_x000D_
}_x000D_
.childTbl th,_x000D_
.childTbl td {_x000D_
  border: 1px solid #d7d7d7;_x000D_
}_x000D_
.scrollData {_x000D_
  width: 690;_x000D_
  height: 150px;_x000D_
  overflow-x: hidden;_x000D_
}
_x000D_
<div class="parentTbl">_x000D_
  <table>_x000D_
    <tr>_x000D_
      <td>_x000D_
        <div class="childTbl">_x000D_
          <table class="childTbl">_x000D_
            <tr>_x000D_
              <th>Header 1</th>_x000D_
              <th>Header 2</th>_x000D_
              <th>Header 3</th>_x000D_
              <th>Header 4</th>_x000D_
              <th>Header 5</th>_x000D_
              <th>Header 6</th>_x000D_
            </tr>_x000D_
          </table>_x000D_
        </div>_x000D_
      </td>_x000D_
    </tr>_x000D_
    <tr>_x000D_
      <td>_x000D_
        <div class="scrollData childTbl">_x000D_
          <table>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Table Data 1</td>_x000D_
              <td>Table Data 2</td>_x000D_
              <td>Table Data 3</td>_x000D_
              <td>Table Data 4</td>_x000D_
              <td>Table Data 5</td>_x000D_
              <td>Table Data 6</td>_x000D_
            </tr>_x000D_
          </table>_x000D_
        </div>_x000D_
      </td>_x000D_
    </tr>_x000D_
  </table>_x000D_
</div>
_x000D_
_x000D_
_x000D_

This is reliable on different browsers, the downside would be having to hard code the table widths.


I was trying to figure this out myself recently, and I came up with a good solution that works perfectly in my browser (Chrome 51) and supports dynamic column widths. I should mention that after I independently derived my answer I also found a similar technique described elsewhere on the web...

The trick is to use two identical tables positioned on top of one another. The lower table is visible and the upper table is invisible expect for the header. The upper table also has pointer-events: none set on the tbody so mouse interactions hit the underneath table. This way the lower table scrolls under the upper table's header.

For everything to layout and resize properly (when the user adjusts screen width for instance), both tables need to have the same scrollbar behavior. However, the upper table's scroll bar is ignored thanks to the pointer-events: none and can be made invisible with:

<style>
    .hiddenScrollbar::-webkit-scrollbar {
        background-color: transparent;
    }
</style>

Here is the complete code:

_x000D_
_x000D_
<html>_x000D_
_x000D_
<head>_x000D_
  <style>_x000D_
    td {_x000D_
      border: 1px solid black; _x000D_
      white-space: nowrap;_x000D_
    }_x000D_
    th {_x000D_
      background-color: gray;_x000D_
      border: 1px solid black;_x000D_
      white-space: nowrap;_x000D_
    }_x000D_
    .hiddenScrollbar::-webkit-scrollbar {_x000D_
      background-color: transparent;_x000D_
    }_x000D_
  </style>_x000D_
</head>_x000D_
_x000D_
<body>_x000D_
  Table test. Use mouse wheel to scroll or scroll to the right to see vertical scroll bar. You can also remove the outermost div if you don't want a horizontal scroll bar._x000D_
  <br/>_x000D_
  <div style="display: inline-block; height: 10em; width: 15em; overflow-x: scroll; overflow-y: hidden">_x000D_
    <div style="position: relative;">_x000D_
      <div style="display: inline-block; position: absolute; left: 0px; top: 0px; height: 10em; overflow-y: scroll">_x000D_
        <table style="border-collapse: collapse;">_x000D_
          <thead>_x000D_
            <tr>_x000D_
              <th>Column 1</th>_x000D_
              <th>Another Column</th>_x000D_
            </tr>_x000D_
          </thead>_x000D_
          <tbody>_x000D_
            <tr>_x000D_
              <td>Data 1</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 2</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 3</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 4</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 5</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 6</td>_x000D_
              <td>12340921375021342354235 very long...</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 7</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 8</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 9</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 10</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 11</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
              <td>Data 12</td>_x000D_
              <td>123409213750213</td>_x000D_
            </tr>_x000D_
          </tbody>_x000D_
        </table>_x000D_
      </div>_x000D_
      <div class="hiddenScrollbar" style="display: inline-block; pointer-events: none; position: relative; left: 0px; top: 0px; height: 10em; overflow-y: scroll">_x000D_
        <table style="border-collapse: collapse;">_x000D_
          <thead style="pointer-events: auto">_x000D_
            <tr>_x000D_
              <th>Column 1</th>_x000D_
              <th>Another Column</th>_x000D_
            </tr>_x000D_
          </thead>_x000D_
          <tbody style="visibility: hidden">_x000D_
            <tr>_x000D_
              <tr>_x000D_
                <td>Data 1</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 2</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 3</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 4</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 5</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 6</td>_x000D_
                <td>12340921375021342354235 very long...</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 7</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 8</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 9</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 10</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 11</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
              <tr>_x000D_
                <td>Data 12</td>_x000D_
                <td>123409213750213</td>_x000D_
              </tr>_x000D_
            </tr>_x000D_
          </tbody>_x000D_
        </table>_x000D_
      </div>_x000D_
    </div>_x000D_
  </div><br/>_x000D_
Stuff after table._x000D_
</body>_x000D_
_x000D_
</html>
_x000D_
_x000D_
_x000D_

Caveats: More work is required to prove this works in other browsers. Also I was rather liberal in mixing inline and stylesheet styles. However I believe the general concept is the best way of doing this since if the target browser supports it. The only functionality/display issues are that if you want a horizontal scroll bar, the vertical scroll bar can get scrolled out of view (as shown in the snippet). You can still scroll with the mouse wheel though. Additionally you can't have a transparent background on the header (otherwise the underneath table would show through). Finally you need a way to generate two identical tables. Personally I am using react.js and it is easy to do it with react, but php or other server-side generation or javascript will also work.


Inspired by @Purag's answer, here's another flexbox solution:

_x000D_
_x000D_
/* basic settings */_x000D_
table { display: flex; flex-direction: column; width: 200px; }_x000D_
tr { display: flex; }_x000D_
th:nth-child(1), td:nth-child(1) { flex-basis: 35%; }_x000D_
th:nth-child(2), td:nth-child(2) { flex-basis: 65%; }_x000D_
thead, tbody { overflow-y: scroll; }_x000D_
tbody { height: 100px; }_x000D_
_x000D_
/* color settings*/_x000D_
table, th, td { border: 1px solid black; }_x000D_
tr:nth-child(odd) { background: #EEE; }_x000D_
tr:nth-child(even) { background: #AAA; }_x000D_
thead tr:first-child { background: #333; }_x000D_
th:first-child, td:first-child { background: rgba(200,200,0,0.7); }_x000D_
th:last-child, td:last-child { background: rgba(255,200,0,0.7); }
_x000D_
<table>_x000D_
    <thead>_x000D_
      <tr>_x000D_
        <th>a_x000D_
        <th>bbbb_x000D_
    <tbody>_x000D_
      <tr>_x000D_
        <td>fooo vsync dynamic_x000D_
        <td>bar_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
      <tr>_x000D_
        <td>a_x000D_
        <td>b_x000D_
  </table>
_x000D_
_x000D_
_x000D_


if it gets rock hard where all the mentioned solutions don't work (as it got for me), try a two-tabled solutioned, as I explained in this answer

https://stackoverflow.com/a/47722456/6488361


I see this thread has been inactive for a while, but this topic interested me and now with some CSS3 selectors, this just became easier (and pretty doable with only CSS).

This solution relies on having a max height of the table container. But it is supported as long as you can use the :first-child selector.

Fiddle here.

If anyone can improve on this answer, please do! I plan on using this solution in a commercial app soon!

HTML

<div id="con">
<table>
    <thead>
        <tr>
            <th>Header 1</th>
            <th>Header 2</th>
            <th>Header 3</th>
        </tr>
    </thead>        
    <tbody>             
    </tbody>
</table>
</div>

CSS

#con{
    max-height:300px;
    overflow-y:auto;
}
thead tr:first-child {
    background-color:#00f;
    color:#fff;
    position:absolute;
}
tbody tr:first-child td{
    padding-top:28px;
}

Surprised a solution using flexbox hasn't been posted yet.

Here's my solution using display: flex and a basic use of :after (thanks to Luggage) to maintain the alignment even with the scrollbar padding the tbody a bit. This has been verified in Chrome 45, Firefox 39, and MS Edge. It can be modified with prefixed properties to work in IE11, and further in IE10 with a CSS hack and the 2012 flexbox syntax.

Note the table width can be modified; this even works at 100% width.

The only caveat is that all table cells must have the same width. Below is a clearly contrived example, but this works fine when cell contents vary (table cells all have the same width and word wrapping on, forcing flexbox to keep them the same width regardless of content). Here is an example where cell contents are different.

Just apply the .scroll class to a table you want scrollable, and make sure it has a thead:

_x000D_
_x000D_
.scroll {_x000D_
  border: 0;_x000D_
  border-collapse: collapse;_x000D_
}_x000D_
_x000D_
.scroll tr {_x000D_
  display: flex;_x000D_
}_x000D_
_x000D_
.scroll td {_x000D_
  padding: 3px;_x000D_
  flex: 1 auto;_x000D_
  border: 1px solid #aaa;_x000D_
  width: 1px;_x000D_
  word-wrap: break-word;_x000D_
}_x000D_
_x000D_
.scroll thead tr:after {_x000D_
  content: '';_x000D_
  overflow-y: scroll;_x000D_
  visibility: hidden;_x000D_
  height: 0;_x000D_
}_x000D_
_x000D_
.scroll thead th {_x000D_
  flex: 1 auto;_x000D_
  display: block;_x000D_
  border: 1px solid #000;_x000D_
}_x000D_
_x000D_
.scroll tbody {_x000D_
  display: block;_x000D_
  width: 100%;_x000D_
  overflow-y: auto;_x000D_
  height: 200px;_x000D_
}
_x000D_
<table class="scroll" width="400px">_x000D_
  <thead>_x000D_
    <tr>_x000D_
      <th>Header</th>_x000D_
      <th>Header</th>_x000D_
      <th>Header</th>_x000D_
      <th>Header</th>_x000D_
      <th>Header</th>_x000D_
      <th>Header</th>_x000D_
    </tr>_x000D_
  </thead>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
  <tr>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
    <td>Data</td>_x000D_
  </tr>_x000D_
</table>
_x000D_
_x000D_
_x000D_


As far as I know, there is no standard way to achieve this with only CSS, although I think there should be. Mozilla browsers used to support fixed headers with a scrolling body, but they've removed it in the last few years.

After researching this a bit, including finding this posting, a friend just developed this solution for me; it uses Javascript but no canned libraries, and the only requirement for the HTML markup is that the table have an id name. Then, at window.onload, to call one Javascript function for each table giving the id, height, and width. If Javascript is disabled at the browser, the whole table is displayed according to its original markup. If Javascript is enabled, the table is fit into the specified height and width, and tbody scrolls, and if thead and tfoot exist, they are fixed at top and bottom.


Another implementation but without any overflow on tbody and dynamic columns. Requires JavaScript though. A container div is used to house the column headings. When the table is scrolled past the view port, a fixed header appears at the top. If table is scrolled horizontally, the fixed header scrolls as well.

Column headings are created using span elements with display: inline-block and a negative margin is used to scroll header horizontally. Also optimized using RequestAnimationFrame to avoid any jank.

function rAF(scrollLeft) {
  var offsetLeft = 0 - scrollLeft;
  $('.hdr__inner span:first-child').css('margin-left', offsetLeft);
}

https://codepen.io/lloydleo/pen/NRpqEE


Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to internet-explorer

Support for ES6 in Internet Explorer 11 The response content cannot be parsed because the Internet Explorer engine is not available, or Flexbox not working in Internet Explorer 11 IE and Edge fix for object-fit: cover; "Object doesn't support property or method 'find'" in IE How to make promises work in IE11 Angular 2 / 4 / 5 not working in IE11 Text in a flex container doesn't wrap in IE11 How can I detect Internet Explorer (IE) and Microsoft Edge using JavaScript? includes() not working in all browsers

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width