How to Make Scrollable Table with Fixed Headers Using CSS

CSS-Only Scrollable Table with fixed headers

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


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>

Table fixed header and scrollable body

Here is the working solution:

table {    width: 100%;}
thead, tbody, tr, td, th { display: block; }
tr:after { content: ' '; display: block; visibility: hidden; clear: both;}
thead th { height: 30px;
/*text-align: left;*/}
tbody { height: 120px; overflow-y: auto;}
thead { /* fallback */}

tbody td, thead th { width: 19.2%; float: left;}
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"/>
<table class="table table-striped"> <thead> <tr> <th>Make</th> <th>Model</th> <th>Color</th> <th>Year</th> </tr> </thead> <tbody> <tr> <td class="filterable-cell">Ford</td> <td class="filterable-cell">Escort</td> <td class="filterable-cell">Blue</td> <td class="filterable-cell">2000</td> </tr> <tr> <td class="filterable-cell">Ford</td> <td class="filterable-cell">Escort</td> <td class="filterable-cell">Blue</td> <td class="filterable-cell">2000</td> </tr> <tr> <td class="filterable-cell">Ford</td> <td class="filterable-cell">Escort</td> <td class="filterable-cell">Blue</td> <td class="filterable-cell">2000</td> </tr> <tr> <td class="filterable-cell">Ford</td> <td class="filterable-cell">Escort</td> <td class="filterable-cell">Blue</td> <td class="filterable-cell">2000</td> </tr> </tbody></table>

How can I make a Table with a Scroll and fixed Header with CSS code, which looks good in any circumstance and on the cellphone too?

You can use the style table-responsive with bootstrap.

 <div class="table-responsive-sm tablealign">
...

Then change table-layout to auto, and assign the mine-width to .table th. Then change the .table width and srollbar's width. Here is all the style.

    <style>
* {
box-sizing: border-box;
}

.wrapper2 {
height: 30vw;
overflow-y: scroll;
}

.card {
background-color: #fff;
border-radius: 10px;
margin: 10% auto;
position: relative;
padding: 34px;
color: #444;
cursor: pointer;
overflow-x: auto;
}

.card:before {
display: block;
position: absolute;
background-color: #ccc;
z-index: -1;
box-shadow: 0 0 40px #999999;
transition: box-shadow .2s ease-in-out;
}

.card.level-3:hover:before {
box-shadow: 0 0 80px #999999;
}

.table {
border-collapse: collapse;
width: 95%;
}

.table td {
overflow: hidden;
word-break: normal;
text-align: center;
}

.table th {
text-align: center;
word-break: normal;
min-width: 100px;
}
.tablealign {
float: right;
width: 100%;
height: 100%;
border-radius: 3px;
padding-bottom: 50px;
}

tbody {
overflow-y: auto;
overflow-x: hidden;
height: 336px;
display: block;
}
tbody::-webkit-scrollbar {
width: 0;
}
thead {
display: table;
width: calc(100% - 17px);
table-layout: auto;
}

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

div.c {
line-height: 1%;
}

</style>

Sample Image

Sample Image

Table with Fixed Header Row and Scrollable Content

<!DOCTYPE HTML>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Table</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
table {
border-collapse: collapse;
width: 1000px;
table-layout: fixed;
}
td,th {
padding: 5px;
min-width: 200px;
border-right: 1px solid #ccc
}
thead tr {
background: #888;
color: #eee;
display: block;
position: relative;
}
tbody {
display: block;
height: 200px;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
}
tbody tr:nth-child(even) {
background: #ddd;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
<tr>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
<td>Content</td>
</tr>
</tbody>
</table>
</body>
</html>

table with fixed thead and scrollable tbody

This solution fulfills all 5 requirements:

table {  width: 100%;}
table, td { border-collapse: collapse; border: 1px solid #000;}
thead { display: table; /* to take the same width as tr */ width: calc(100% - 17px); /* - 17px because of the scrollbar width */}
tbody { display: block; /* to enable vertical scrolling */ max-height: 200px; /* e.g. */ overflow-y: scroll; /* keeps the scrollbar even if it doesn't need it; display purpose */}
th, td { width: 33.33%; /* to enable "word-break: break-all" */ padding: 5px; word-break: break-all; /* 4. */}
tr { display: table; /* display purpose; th's border */ width: 100%; box-sizing: border-box; /* because of the border (Chrome needs this line, but not FF) */}
td { text-align: center; border-bottom: none; border-left: none;}
<table>   <thead>     <tr>      <th>Table Header 1</th>      <th>Table Header 2</th>      <th>Table Header 3</th>    </tr>   </thead>  <tbody>    <tr>      <td>Data1111111111111111111111111</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data2222222222222222222222222</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data3333333333333333333333333</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>    <tr>      <td>Data</td>      <td>Data</td>      <td>Data</td>    </tr>  </tbody></table>

table with sticky header and horizontal scroll

As per the MDN documentation:

a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow is hidden, scroll, auto, or overlay), even if that ancestor isn't the nearest actually scrolling ancestor.

There's an active GitHub issue discussing this on the W3C repo, which has been running since 2017. There have been various workarounds suggested, but they all seem to rely on adding a fixed height to the table / table container, or using Javascript as in this answer.

At least for the moment, this is not something that's supported natively.



Related Topics



Leave a reply



Submit