HTML table with fixed header and footer and scrollable body without fixed widths
I want to to create a table with fixed thead
and tfoot
and a scrollable tbody
!
I’ve tried several approaches, both CSS only and CSS + Javascript, but they are all weak and unreliable and I can easily break them by changing the markup in the demo.
What I want is a way to have the table to behave like a table, this means that the browser will automatically adjust columns based on the content (both at page load that in case of window resize) and that in these scenarios:
-
if the content of the column’s header (
thead > tr > th
) is larger than the content of the column’s body (tbody > tr > td
) and larger than the content of the column’s footer (tfoot > tr > td
) the column should resize based on the size of the column’s header -
if the content of the column’s body (
tbody > tr > td
) is larger than the content of the column’s header (thead > tr > th
) and larger than the content of the column’s footer (tfoot > tr > td
) the column should resize based on the size of the column’s body -
if the content of the column’s footer (
tfoot > tr > td
) is larger than the content of the column’s header (thead > tr > th
) and larger than the content of the column’s body (tbody > tr > td
) the column should resize based on the size of the column’s footer
The table
below should clarify the scenarios:
<table>
<thead>
<tr>
<th>Header one *leads the width* (case 1)</th>
<th>Header two</th>
<th>Header three</th>
</tr>
</thead>
<tbody>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer one</td>
<td>Footer two</td>
<td>Footer three *leads the width* (case 3)</td>
</tr>
</tfoot>
</table>
I want a clean (as possible) and reliable solution that will work for the different scenarios, possibly CSS only but also JavaScript is OK (vanilla and clean JavaScript, not jQuery plugins).
I don’t care about old browser support (it would be great to have it or at least to reach a solution which can degrade gracefully on old browser but it’s optional)… I can even accept to use div
s instead of table nodes if the final solution works as expected… so in 2016, with modern browser and CSS is this possible somehow?!
EDIT:
The body should scroll vertically and the table may have any number of columns
UPDATE:
I came up with this solution: https://codepen.io/daveoncode/pen/LNomBE
but I’m still not 100% satisfied. The main issue is that I can’t set different backgrounds for header and footer cells.
UPDATE 2:
it works now!
HTML table with fixed header and footer and scrollable body without fixed widths using CSS only.
This is simple logic we can use table header put position: sticky method below the example:
table th {
position:sticky;
top: 0;
}
UPDATE 2022
Nowadays and for awhile browsers have full support for position: sticky
So we can apply it to thead th
and tfoot tr
body {
margin: 0
}
table {
width: 100%
}
thead th,
tfoot tr {
position: sticky;
background: red
}
thead th {
top: 0
}
tfoot tr {
bottom: 0
}
th,
td {
font-size: 12px;
text-align: center
}
/*give some space between thead and tfoot*/
tbody tr:first-of-type td {
padding-top: 10px;
}
tbody tr:last-of-type td {
padding-bottom: 10px;
}
<table>
<thead>
<tr>
<th>Header one *leads the width* (case 1)</th>
<th>Header two</th>
<th>Header three</th>
</tr>
</thead>
<tbody>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer one</td>
<td>Footer two</td>
<td>Footer three *leads the width* (case 3)</td>
</tr>
</tfoot>
</table>
OLD Answer
you can achieve what you want by using a wrapper to your table (div
) and make the tr
from thead
and tfoot
a position:absolute
body {
margin: 0
}
div {
max-height: 500px;
overflow-y: auto;
}
table {
width: 100%
}
thead tr,
tfoot tr {
position: absolute;
left: 0;
right: 15px;
/* to not cover the scrollbar*/
background: red
}
thead th,
tfoot td {
display: inline-block;
}
thead tr {
top: 0
}
tfoot tr {
top: 500px/* same value has max-height from div */
}
th,
td {
width: calc((100%/3) - 5px);
font-size: 12px;
text-align: center
}
/*give some space between thead and tfoot*/
tbody tr:first-of-type td {
padding-top: 35px;
}
tbody tr:last-of-type td {
padding-bottom: 35px;
}
<div>
<table>
<thead>
<tr>
<th>Header one *leads the width* (case 1)</th>
<th>Header two</th>
<th>Header three</th>
</tr>
</thead>
<tbody>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer one</td>
<td>Footer two</td>
<td>Footer three *leads the width* (case 3)</td>
</tr>
</tfoot>
</table>
</div>
I finally implemented a working solution!
The relevant CSS is the following:
.wrapper {
width: 90%;
position: relative;
border: 1px solid #000;
background: #efefef;
overflow: hidden;
border-radius: 7px;
}
.container {
overflow-y: auto;
height: 200px;
border-top: 41px solid transparent;
border-bottom: 41px solid transparent;
}
table {
border-spacing: 0;
border-collapse: collapse;
width: 100%;
}
td + td {
border-left: 1px solid #fff;
}
td, th {
border-bottom: 1px solid #fff;
background: #efefef;
padding: 10px;
}
thead tr th,
tfoot tr td {
height: 0;
line-height: 0;
margin: 0;
padding-top: 0;
padding-bottom: 0;
color: transparent;
border: none;
white-space: nowrap;
}
thead tr th div,
tfoot tr td div {
position: absolute;
color: #fff;
height: 20px;
padding: 10px;
margin-left: -10px;
line-height: normal;
width: 100%;
z-index: 2;
text-align: left;
font-weight: bold;
}
thead tr th div {
border-left: 1px solid #000;
border-bottom: 1px solid #000;
}
tfoot tr td div {
border-top: 1px solid #000;
}
tfoot tr td div.c1,
thead tr th div.c1 {
background: violet;
}
tfoot tr td div.c2,
thead tr th div.c2 {
background: green;
}
tfoot tr td div.c3,
thead tr th div.c3 {
background: yellow;
}
thead tr th div {
top: 0;
}
tfoot tr td div {
bottom: 0;
}
thead tr th:first-child div,
tfoot tr td:first-child div {
border-left: none;
}
And this is the markup:
<div class="wrapper">
<div class="container">
<table>
<thead>
<tr>
<th>
Header one *leads the width* (case 1)
<div class="c1">
Header one *leads the width* (case 1)
</div>
</th>
<th>
Header two
<div class="c2">
Header two
</div>
</th>
<th>
Header three
<div class="c3">
Header three
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three [first]</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three</td>
</tr>
<tr>
<td>Column one</td>
<td>Column two *leads the width* (case 2)</td>
<td>Column three [LATEST]</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
Footer one
<div class="c1">
Footer one
</div>
</td>
<td>
Footer two
<div class="c2">Footer two</div>
</td>
<td>
Footer three *leads the width* (case 3)
<div class="c3">Footer three *leads the width* (case 3)</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
It works on Chrome, Firefox, Safari and IE11 (I don’t know how it behaves on older browsers).
See it on codepen: https://codepen.io/daveoncode/pen/LNomBE
$.ajax({
url: "upload.php",
type: "POST",
data: fd,
processData: false, // tell jQuery not to process the data
contentType: false // tell jQuery not to set contentType
}).done(function( data ) {
console.log("PHP Output:");
console.log( data );
});
return false;
}