Viewed only CSS table with fixed headers

I have a solution with which I can create scrollable tables with a fixed header / footer using junior jQuery and CSS, but I am looking for a way to make this a CSS-only cross-browser compatible solution.

To be clear, I'm trying to use the only a table tag (and these are valid subtags, colgroup , col , thead , tbody , tfoot , tr , th , td ), but accept a set of CSS rules that will meet the following conditions:

  • It is necessary to maintain column alignment between header / footer / content lines
  • Must allow the header / footer to remain fixed while the content scrolls vertically.
  • No need to use jQuery or other JavaScript to provide functionality
  • Only use the tags indicated above.

This sample code: http://jsfiddle.net/TroyAlford/SNKfd/ shows my current approach. Most JS is just filling the table with random values, but the last part is what causes the left / right scroll.

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

NOTE. The above example does not render correctly in IE and requires jQuery to provide horizontal scrolling. In any case, I do not need horizontal scrolling, so this is normal if the solution does not.

+60
html css internet-explorer css3
Aug 09 '12 at 20:00
source share
13 answers

This answer will be used as a placeholder for the not fully supported position: sticky and will be updated over time. It is currently recommended that you do not use the built-in implementation of this in a production environment.

See Current Support: https://caniuse.com/#feat=css-sticky




Using position: sticky

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

A sticky positioned box is positioned similarly to a relatively positioned block, but the offset is calculated with reference to the nearest ancestor using the scroll window or viewport if the ancestor does not have a scroll box.

This accurately describes the behavior of the 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 the W3C . However, both Chrome , IE, and Edge have problems setting label properties for these tags. At the moment, there is also no priority in resolving this issue.

It seems to work for the table element by assigning the sticky property to table-cell. In this case, the <th> cell.

Since the table is not a block element that takes into account the static size that you assign to it, it is best to use a wrapper element to determine 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> shell to detect scroll overflow done with a static height of 150px . This, of course, can be of any size. Now that the scroll box is defined, the sticky <th> elements will respond to the โ€œclosest ancestor using the scroll boxโ€, which is a wrapper for the div.




Using position: sticky polyphony

Unsupported devices can use a poly regiment that implements behavior through code. An example is stickybits , which resembles the same behavior as browser-based position: sticky .

Polyfill example: http://jsfiddle.net/7UZA4/6957/

+17
Sep 21 '18 at 13:13
source share

A surprise solution using flexbox has not yet been published.

Here is my solution using display: flex and basic use :after (thanks to Luggage ) to maintain alignment even when filling the tbody bit. This is confirmed in Chrome 45, Firefox 39, and MS Edge . It can be modified using prefix properties to work in IE11 and later in IE10 using CSS hack and 2012 flexbox syntax .

Please note that the width of the table can be changed; it even works with a width of 100% .

The only caveat is that all cells in the table should have the same width. The following is a clearly contrived example, but it works fine when the contents of a cell change (all the cells in the table have the same width and word wrap, causing flexbox to keep them the same width regardless of the content). Here is an example when the contents of a cell are different.

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

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

As far as I know, there is no standard way to achieve this with CSS alone, although I think it should be. Mozilla browsers use fixed scrolling body headers, but they have removed it over the past few years.

After studying this a bit, including searching for this publication, a friend just developed this solution for me; it uses Javascript, but does not have canned libraries, and the only requirement for HTML markup is that the table has an identifier. Then, in window.onload, one Javascript function is called for each table with an identifier, height and width. If Javascript is disabled in the browser, the entire table is displayed according to its original layout. If Javascript is enabled, the table fits into the specified height and width, and the throne scrolls, and if there are tags and tfoot, they are fixed at the top and bottom.

+7
Oct 23 '13 at 9:11
source share

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

 /* basic settings */ table { display: flex; flex-direction: column; width: 200px; } tr { display: flex; } th:nth-child(1), td:nth-child(1) { flex-basis: 35%; } th:nth-child(2), td:nth-child(2) { flex-basis: 65%; } thead, tbody { overflow-y: scroll; } tbody { height: 100px; } /* color settings*/ table, th, td { border: 1px solid black; } tr:nth-child(odd) { background: #EEE; } tr:nth-child(even) { background: #AAA; } thead tr:first-child { background: #333; } th:first-child, td:first-child { background: rgba(200,200,0,0.7); } th:last-child, td:last-child { background: rgba(255,200,0,0.7); } 
 <table> <thead> <tr> <th>a <th>bbbb <tbody> <tr> <td>fooo vsync dynamic <td>bar <tr> <td>a <td>b <tr> <td>a <td>b <tr> <td>a <td>b <tr> <td>a <td>b <tr> <td>a <td>b <tr> <td>a <td>b </table> 
+7
Sep 28 '16 at 14:18
source share

Ive easily executed this code:

So you have a structure like this:

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

just enter 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:

Firstly, this property is new. It is not supported at all, except for beta versions of Webkit-based browsers. So caution. Again, if you really want your users to use sticky headers, go to the javascript implementation.

Secondly, if you use it, you need to enable vendor prefixes. Perhaps the position: sticky will work one day. At this point you need to use the position: -webkit-sticky (and others, check the css block again in this post).

Thirdly, at the moment there are no default settings, so you need to at least enable top: 0; in the same css declaration as position: -webkit-sticky. Otherwise, itll just scroll the screen.

+5
Apr 20 '17 at 20:25
source share

If you have the opportunity to set a fixed width for table cells (and a fixed height for the header), you can use the position: fixed option:

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

You just need to paste it in the iframe . You can also have horizontal scroll by specifying an iframe scroll bar (I think).




Change 2015

If you can live with a predetermination of the width of your table cells (in percent), then here is a little more elegant (only CSS-solution):

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

+3
Aug 11 2018-12-12T00:
source share

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

+3
Mar 30 '17 at 7:42 on
source share

I see that this thread has been inactive for a while, but this question interested me now with some CSS3 selectors, it has become easier (and quite feasible only with CSS).

This decision depends on the maximum height of the table container. But it is supported as long as you can use the :first-child selector.

Fiddle here .

If someone can improve this answer, do it! I plan to use this solution in a commercial application in the near future!

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; } 
+1
Jun 27 '14 at 13:11
source share

I had the same problem, and after two days of research, I found this solution from Ksesocss, which is suitable for me and maybe good for you too. It allows you to capture the title and dynamic width and uses only CSS. The only problem is that the source is in Spanish, but you can find the html and css code there.

This is a link:

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

I hope this helps

+1
Jul 03 '15 at 8:28
source share

if it gets tough when all the solutions mentioned don't work (how it turned out for me), try a solution with two options, as I explained in this answer

stack overflow

+1
Dec 08 '17 at 21:37
source share

As I recently needed 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 column headings. The second row contains table 3 (child 2) for the scroll content.

It should be noted that childTbl must be 25px shorter than parentTbl in order for the scroller to display correctly.

This is the source where I got this idea from. I made HTML5 friendly without outdated tags and inline CSS.

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

This is a reliable solution for different browsers. A disadvantage would be hard coding of the width of the table.

0
Mar 27 '15 at 15:30
source share

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

The trick is to use two identical tables located one above the other. The bottom table is visible, and the top table is invisible to the header. The top table also has pointer-events: none mounted on the body, so mouse interactions fall into the bottom table. Thus, the bottom table scrolls under the heading of the top table.

For everything you need for layout and resizing (when the user adjusts the screen width, for example), both tables should have the same scroll behavior. However, the top scroll bar of the table is ignored thanks to pointer-events: none and can be invisible with:

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

Here is the complete code:

 <html> <head> <style> td { border: 1px solid black; white-space: nowrap; } th { background-color: gray; border: 1px solid black; white-space: nowrap; } .hiddenScrollbar::-webkit-scrollbar { background-color: transparent; } </style> </head> <body> 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. <br/> <div style="display: inline-block; height: 10em; width: 15em; overflow-x: scroll; overflow-y: hidden"> <div style="position: relative;"> <div style="display: inline-block; position: absolute; left: 0px; top: 0px; height: 10em; overflow-y: scroll"> <table style="border-collapse: collapse;"> <thead> <tr> <th>Column 1</th> <th>Another Column</th> </tr> </thead> <tbody> <tr> <td>Data 1</td> <td>123409213750213</td> </tr> <tr> <td>Data 2</td> <td>123409213750213</td> </tr> <tr> <td>Data 3</td> <td>123409213750213</td> </tr> <tr> <td>Data 4</td> <td>123409213750213</td> </tr> <tr> <td>Data 5</td> <td>123409213750213</td> </tr> <tr> <td>Data 6</td> <td>12340921375021342354235 very long...</td> </tr> <tr> <td>Data 7</td> <td>123409213750213</td> </tr> <tr> <td>Data 8</td> <td>123409213750213</td> </tr> <tr> <td>Data 9</td> <td>123409213750213</td> </tr> <tr> <td>Data 10</td> <td>123409213750213</td> </tr> <tr> <td>Data 11</td> <td>123409213750213</td> </tr> <tr> <td>Data 12</td> <td>123409213750213</td> </tr> </tbody> </table> </div> <div class="hiddenScrollbar" style="display: inline-block; pointer-events: none; position: relative; left: 0px; top: 0px; height: 10em; overflow-y: scroll"> <table style="border-collapse: collapse;"> <thead style="pointer-events: auto"> <tr> <th>Column 1</th> <th>Another Column</th> </tr> </thead> <tbody style="visibility: hidden"> <tr> <tr> <td>Data 1</td> <td>123409213750213</td> </tr> <tr> <td>Data 2</td> <td>123409213750213</td> </tr> <tr> <td>Data 3</td> <td>123409213750213</td> </tr> <tr> <td>Data 4</td> <td>123409213750213</td> </tr> <tr> <td>Data 5</td> <td>123409213750213</td> </tr> <tr> <td>Data 6</td> <td>12340921375021342354235 very long...</td> </tr> <tr> <td>Data 7</td> <td>123409213750213</td> </tr> <tr> <td>Data 8</td> <td>123409213750213</td> </tr> <tr> <td>Data 9</td> <td>123409213750213</td> </tr> <tr> <td>Data 10</td> <td>123409213750213</td> </tr> <tr> <td>Data 11</td> <td>123409213750213</td> </tr> <tr> <td>Data 12</td> <td>123409213750213</td> </tr> </tr> </tbody> </table> </div> </div> </div><br/> Stuff after table. </body> </html> 

Caveats: additional work is required to prove that this works in other browsers. I was also quite liberal in mixing inline styles and styles. However, I believe that a general concept is the best way to do this, since the target browser supports it. The only problems with the functionality / display is that if you want a horizontal scrollbar, the vertical scrollbar can scroll from the view (as shown in the snippet). However, you can scroll with the mouse wheel. In addition, you cannot have a transparent background in the header (otherwise the bottom table will be displayed). Finally, you need a way to create two identical tables. Personally, I use response.js and itโ€™s easy to react with it, but php or another server generation or javascript will work as well.

0
Aug 04 '16 at 18:07
source share

Another implementation, but without overflow on tbody and dynamic columns. JavaScript required. The div container is used to place column headings. When the table scrolls past the view port, a fixed header is displayed at the top. If the table scrolls horizontally, the fixed title also scrolls.

Column headers are created using span elements with display: inline-block , and negative margin is used to scroll the header horizontally. It is also optimized using RequestAnimationFrame to avoid using jank.

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

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

0
Sep 24 '16 at 23:41
source share



All Articles