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:
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.
To freeze the row/column we can use a simple HTML table and CSS. HTML: In HTML we can define the header row by <th> tag or we can use <td> tag also. Below example is using the <th> tag. We also put the table in DIV element to see the horizontal and vertical scrollbar by setting the overflow property of the DIV element.
This answer will be used as a placeholder for the not fully supported position: sticky
and will be updated over time. It is currently advised to not use the native implementation of this in a production environment.
See this for the current support: https://caniuse.com/#feat=css-sticky
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.
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.
position: sticky
polyfill
Non-supported devices can make use of a polyfill, which implements the behavior through code. An example is stickybits, which resembles the same behavior as the browser's implemented position: sticky
.
Example with polyfill: http://jsfiddle.net/7UZA4/6957/
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
:
.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-word;
}
.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>
Inspired by @Purag's answer, here's 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>
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.
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
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;
}
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With