Table fixed header and scrollable body
Sophia Terry
I am trying to make a table with fixed header and a scrollable content using the bootstrap 3 table. Unfortunately the solutions I have found does not work with bootstrap or mess up the style.
Here there is a simple bootstrap table, but for some reason to me unknown the height of the tbody is not 10px.
height: 10px !important; overflow: scroll;Example:
<link rel="stylesheet" type="text/css" href="//">
<table> <thead> <tr> <th>Make</th> <th>Model</th> <th>Color</th> <th>Year</th> </tr> </thead> <tbody> <tr> <td>111 Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> </tbody>
</table> 6 29 Answers
Fixed table head - CSS-only
Simply position: sticky; top: 0; your th elements. (Chrome, FF, Edge)
.tableFixHead { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
/* Just common table stuff. Really. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th { background:#eee; }<div> <table> <thead> <tr><th>TH 1</th><th>TH 2</th></tr> </thead> <tbody> <tr><td>A1</td><td>A2</td></tr> <tr><td>B1</td><td>B2</td></tr> <tr><td>C1</td><td>C2</td></tr> <tr><td>D1</td><td>D2</td></tr> <tr><td>E1</td><td>E2</td></tr> </tbody> </table>
</div>For both sticky vertical TH and horizontal TH columns (inside TBODY):
.tableFixHead { overflow: auto; height: 100px; width: 240px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead tbody th { position: sticky; left: 0; }.tableFixHead { overflow: auto; height: 100px; width: 240px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead tbody th { position: sticky; left: 0; }
/* Just common table stuff. Really. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; white-space: nowrap; }
th { background:#eee; }<div> <table> <thead> <tr><th></th><th>TH 1</th><th>TH 2</th></tr> </thead> <tbody> <tr><th>Foo</th><td>Some long text lorem ipsum</td><td>Dolor sit amet</td></tr> <tr><th>Bar</th><td>B1</td><td>B2</td></tr> <tr><th>Baz</th><td>C1</td><td>C2</td></tr> <tr><th>Fuz</th><td>D1</td><td>D2</td></tr> <tr><th>Zup</th><td>E1</td><td>E2</td></tr> </tbody> </table>
</div>TH borders problem fix
Since border cannot be painted properly on a translated TH element,
to recreate and render "borders" use the box-shadow property:
/* Borders (if you need them) */
.tableFixHead,
.tableFixHead td { box-shadow: inset 1px -1px #000;
}
.tableFixHead th { box-shadow: inset 1px 1px #000, 0 1px #000;
}.tableFixHead { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
/* Just common table stuff. Really. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th { background:#eee; }
/* Borders (if you need them) */
.tableFixHead,
.tableFixHead td { box-shadow: inset 1px -1px #000;
}
.tableFixHead th { box-shadow: inset 1px 1px #000, 0 1px #000;
}<div> <table> <thead> <tr><th>TH 1</th><th>TH 2</th></tr> </thead> <tbody> <tr><td>A1</td><td>A2</td></tr> <tr><td>B1</td><td>B2</td></tr> <tr><td>C1</td><td>C2</td></tr> <tr><td>D1</td><td>D2</td></tr> <tr><td>E1</td><td>E2</td></tr> </tbody> </table>
</div>TH sticky not working fix
Ensure that parent-elements of "th" element, at least till table element (inclusive), do Not set overflow related styles (e.g. overflow, overflow-x, overflow-y).
For more see Why is 'position: sticky' not working?
Fixed table head - using JS. (IE)
You can use a bit of JS and translateY the th elements
jQuery example
var $th = $('.tableFixHead').find('thead th')
$('.tableFixHead').on('scroll', function() { $th.css('transform', 'translateY('+ this.scrollTop +'px)');
});.tableFixHead { overflow-y: auto; height: 100px; }
/* Just common table stuff. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th { background:#eee; }<div> <table> <thead> <tr><th>TH 1</th><th>TH 2</th></tr> </thead> <tbody> <tr><td>A1</td><td>A2</td></tr> <tr><td>B1</td><td>B2</td></tr> <tr><td>C1</td><td>C2</td></tr> <tr><td>D1</td><td>D2</td></tr> <tr><td>E1</td><td>E2</td></tr> </tbody> </table>
</div>
<script src=""></script>Or plain ES6 if you prefer (no jQuery required):
// Fix table head
function tableFixHead (e) { const el = e.target, sT = el.scrollTop; el.querySelectorAll("thead th").forEach(th => th.style.transform = `translateY(${sT}px)` );
}
document.querySelectorAll(".tableFixHead").forEach(el => el.addEventListener("scroll", tableFixHead)
); 12 For a pure CSS approach you'd need a container with overflow-y: auto; and decide how to hide scrolled/overflown rows:
- Overlay by a non-transparent sticky header (
position: sticky; top: 0; z-index: 1;), like in the @RokoCBuljan's answer. - Toggle visibility of the rows (by setting
tr:afterproperties as per below).
Note that the container can be an external <div>, or the <table> itself, or a part of it (e.g. <tbody>). For the later two you gotta set display: block; so effectively they're treated as divs.
See below a modified @giulio's solution:
:root { --height-height: 150px; /* cell width has to reserve some space for scrolling. Hence the sum < 100% */ --cell-width: 85px;
}
.header-fixed { width: 200px;
}
/* Treat all as divs */
.header-fixed > thead,
.header-fixed > tbody,
.header-fixed > thead > tr,
.header-fixed > tbody > tr,
.header-fixed > thead > tr > th,
.header-fixed > tbody > tr > td { display: block;
}
/* Prevent header to wrap */
.header-fixed > thead > tr > th { white-space: nowrap; background-color: lightgrey;
}
.header-fixed > tbody > tr:after,
.header-fixed > thead > tr:after { content: ' '; display: block; visibility: hidden; clear: both;
}
.header-fixed > tbody { overflow-y: auto; height: var(--height-height);
}
.header-fixed > tbody > tr > td,
.header-fixed > thead > tr > th { width: var(--cell-width); border: thin solid grey; float: left;
}<table> <thead> <tr> <th>Header 1</th> <th>Header 2</th> </tr> </thead> <tbody> <tr> <td>cell 11</td> <td>cell 12</td> </tr> <tr> <td>cell 21</td> <td>cell 22</td> </tr> <tr> <td>cell 31</td> <td>cell 32</td> </tr> <tr> <td>cell 41</td> <td>cell 42</td> </tr> <tr> <td>cell 51</td> <td>cell 52</td> </tr> <tr> <td>cell 61</td> <td>cell 62</td> </tr> <tr> <td>cell 71</td> <td>cell 72</td> </tr> <tr> <td>cell 81</td> <td>cell 82</td> </tr> <tr> <td>cell 91</td> <td>cell 92</td> </tr> </tbody>
</table>Note: If you've got >2 columns, you need to fiddle with the var(--cell-width) variable accordingly.
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="" rel="stylesheet"/>
<table> <thead> <tr> <th>Make</th> <th>Model</th> <th>Color</th> <th>Year</th> </tr> </thead> <tbody> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> <tr> <td>Ford</td> <td>Escort</td> <td>Blue</td> <td>2000</td> </tr> </tbody>
</table>Link to jsfiddle
5Update
For newer and still maintained library try jquery.floatThead (as mentioned by Bob Jordan in the comment) instead.
Old Answer
This is a very old answer, the library mentioned below no longer maintained.
I am using StickyTableHeaders on GitHub and it works like charm!
I had to add this css to make the header not transparent though.
table#stickyHeader thead { border-top: none; border-bottom: none; background-color: #FFF;
} 5 Don't need the wrap it in a div...
CSS:
tr {
width: 100%;
display: inline-table;
table-layout: fixed;
}
table{ height:300px; // <-- Select the height of the table display: block;
}
tbody{ overflow-y: scroll; height: 200px; // <-- Select the height of the body width: 100%; position: absolute;
}Bootply :
6It is easier with css
table tbody { display:block; max-height:450px; overflow-y:scroll; }
table thead, table tbody tr { display:table; width:100%; table-layout:fixed; } 2 By far the best solution I've seen that is CSS only, with good cross browser support, and no alignment issues is this solution from codingrabbithole
table { width: 100%;
}
thead, tbody tr { display: table; width: 100%; table-layout: fixed;
}
tbody { display: block; overflow-y: auto; table-layout: fixed; max-height: 200px;
} 5 table { overflow-y: auto; height: 50vh; /* !!! HEIGHT MUST BE IN [ vh ] !!! */
}
thead th { position: sticky; top: 0;
} <table> <thead> <tr><th>TH 1</th><th>TH 2</th></tr> </thead> <tbody> <tr><td>A1</td><td>A2</td></tr> <tr><td>B1</td><td>B2</td></tr> <tr><td>C1</td><td>C2</td></tr> <tr><td>D1</td><td>D2</td></tr> <tr><td>E1</td><td>E2</td></tr> <tr><td>F1</td><td>F2</td></tr> <tr><td>G1</td><td>G2</td></tr> <tr><td>H1</td><td>H2</td></tr> <tr><td>I1</td><td>I2</td></tr> <tr><td>J1</td><td>J2</td></tr> <tr><td>K1</td><td>K2</td></tr> <tr><td>L1</td><td>L2</td></tr> <tr><td>M1</td><td>M2</td></tr> <tr><td>N1</td><td>N2</td></tr> <tr><td>O1</td><td>O2</td></tr> <tr><td>P1</td><td>P2</td></tr> <tr><td>Q1</td><td>Q2</td></tr> <tr><td>R1</td><td>R2</td></tr> <tr><td>S1</td><td>S2</td></tr> <tr><td>T1</td><td>T2</td></tr> <tr><td>U1</td><td>U2</td></tr> <tr><td>V1</td><td>V2</td></tr> <tr><td>W1</td><td>W2</td></tr> <tr><td>X1</td><td>X2</td></tr> <tr><td>Y1</td><td>Y2</td></tr> <tr><td>Z1</td><td>Z2</td></tr> </tbody> </table>You don't need js. Important is to set table height in [vh]
1Late to the party (Story of my life), but since this is the first result on google, and none of the above got me working, here's my code
/*Set a min width where your table start to look like crap*/
table { min-width: 600px; }
/*The next 3 sections make the magic happen*/
thead, tbody tr { display: table; width: 100%; table-layout: fixed;
}
tbody { display: block; max-height: 200px; overflow-x: hidden; overflow-y: scroll;
}
td { overflow: hidden; text-overflow: ellipsis;
}
/*Use the following to make sure cols align correctly*/
table, tr, th, td { border: 1px solid black; border-collapse: collapse;
}
/*Set your columns to where you want them to be, skip the one that you can have resize to any width*/ th:nth-child(1), td:nth-child(1) { width: 85px;
}
th:nth-child(2), td:nth-child(2) { width: 150px;
}
th:nth-child(4), td:nth-child(4) { width: 125px;
}
th:nth-child(5) { width: 102px;
}
td:nth-child(5) { width: 85px;
} 2 In my eyes, one of the best plugins for jQuery is DataTables.
It also has an extension for fixed header, and it is very easily implemented.
Taken from their site:
HTML:
<table cellspacing="0" width="100%"> <thead> <tr> <th>Name</th> <th>Position</th> <th>Office</th> <th>Age</th> <th>Start date</th> <th>Salary</th> </tr> </thead> <tfoot> <tr> <th>Name</th> <th>Position</th> <th>Office</th> <th>Age</th> <th>Start date</th> <th>Salary</th> </tr> </tfoot> <tbody> <tr> <td>Tiger Nixon</td> <td>System Architect</td> <td>Edinburgh</td> <td>61</td> <td>2011/04/25</td> <td>$320,800</td> </tr> <tr> <td>Garrett Winters</td> <td>Accountant</td> <td>Tokyo</td> <td>63</td> <td>2011/07/25</td> <td>$170,750</td> </tr> <tr> <td>Ashton Cox</td> <td>Junior Technical Author</td> <td>San Francisco</td> <td>66</td> <td>2009/01/12</td> <td>$86,000</td> </tr> </tbody>
</table>JavaScript:
$(document).ready(function() { var table = $('#example').DataTable(); new $.fn.dataTable.FixedHeader( table );
} );But the simplest you can have for just making a scrollable <tbody> is:
//configure table with fixed header and scrolling rows
$('#example').DataTable({ scrollY: 400, scrollCollapse: true, paging: false, searching: false, ordering: false, info: false
}); 1 The latest addition position:'sticky' would be the simplest solution here
.outer{ overflow-y: auto; height:100px; }
.outer table{ width: 100%; table-layout: fixed; border : 1px solid black; border-spacing: 1px;
}
.outer table th { text-align: left; top:0; position: sticky; background-color: white;
} <div class = "outer"> <table> <tr > <th>col1</th> <th>col2</th> <th>col3</th> <th>col4</th> <th>col5</th> <tr> <tr > <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> <tr> <tr > <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> <tr> <tr > <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> <tr> </table> </div> 1 Multiple scrollable table support in a single window.
Pure CSS & No fixed or sticky.
I am searching fixed table header with auto "td" and "th" width for years. Finally i coded something, it's work well for me but i am not sure is it work well for everyone.
Problem 1: We can't set table or tbody height while have tons of "tr" it's because of default table properties.
Solution: Set table a display property.
Problem 2: When we set a display property, the width of "td" elements can't be equal width of "th" elements. And it is hard to fill elements properly in full width table like 100%.
Solution: CSS "flex" is very good solution for width and fill set-ups, so we will build our tbody and thead elements with CSS "flex".
.ea_table { border: 1px solid #ddd; display: block; background: #fff; overflow-y: hidden; box-sizing: border-box; float: left; height:auto; width: 100%;
}
.ea_table tbody, thead { flex-direction: column; display: flex;
}
.ea_table tbody { height: 300px; overflow: auto;
}
.ea_table thead { border-bottom: 1px solid #ddd;
}
.ea_table tr { display: flex;
}
.ea_table tbody tr:nth-child(2n+1) { background: #f8f8f8; }
.ea_table td, .ea_table th { text-align: left; font-size: 0.75rem; padding: 1.5rem; flex: 1;
}<table> <thead> <tr> <th>Something Long</th> <th>Something </th> <th>Something Very Long</th> <th>Something Long</th> <th>Some</th> </tr> </thead> <tbody> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem </td> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> <tr> <td> Lorem Ipsum Dolar Sit Amet</td> <td> Lorem </td> <td> Lorem Ipsum </td> <td> Lorem </td> <td> Lorem Ipsum Dolar </td> </tr> </tbody> </table> You should try with "display:block;" to tbody, because now it's inline-block and in order to set height, the element should be "block"
2I had a lot of trouble getting the stickytableheaders library to work. Doing a bit more searching, I found floatThead is an actively maintained alternative with recent updates and better documentation.
I used the floatThead jQuery plugin ()
docs say it works with Bootstrap 3 tables and I can say it also works with Bootstrap 4 tables with or without the table-responsive helper.
Using the plugin is as simple as this:
HTML (vanilla bootstrap table markup)
<div> <table> <thead>...</thead> <tbody>...</tbody> </table>
</div>Plugin Initialization:
$(document).ready(function() { var tbl=$('#myTable'); tbl.floatThead({ responsiveContainer: function(tbl) { return tbl.closest('.table-responsive'); } });
});Full disclaimer:I am not associated with the plugin in any way. I happened to find it after hours of trying lots of other solutions posted here and elsewhere.
0First add some markup for a bootstrap table. Here I created a striped table but also have added a custom table class .table-scroll which adds vertical scroll bar to the table and makes the table header fixed while scrolling down.
<div> <table> <thead> <tr> <th>#</th> <th>First Name</th> <th>Last Name</th> <th>County</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Andrew</td> <td>Jackson</td> <td>Washington</td> </tr> <tr> <td>2</td> <td>Thomas</td> <td>Marion</td> <td>Jackson</td> </tr> <tr> <td>3</td> <td>Benjamin</td> <td>Warren</td> <td>Lincoln</td> </tr> <tr> <td>4</td> <td>Grant</td> <td>Wayne</td> <td>Union</td> </tr> <tr> <td>5</td> <td>John</td> <td>Adams</td> <td>Marshall</td> </tr> <tr> <td>6</td> <td>Morgan</td> <td>Lee</td> <td>Lake</td> </tr> <tr> <td>7</td> <td>John</td> <td>Henry</td> <td>Brown</td> </tr> <tr> <td>8</td> <td>William</td> <td>Jacob</td> <td>Orange</td> </tr> <tr> <td>9</td> <td>Kelly</td> <td>Davidson</td> <td>Taylor</td> </tr> <tr> <td>10</td> <td>Colleen</td> <td>Hurst</td> <td>Randolph</td> </tr> <tr> <td>11</td> <td>Rhona</td> <td>Herrod</td> <td>Cumberland</td> </tr> <tr> <td>12</td> <td>Jane</td> <td>Paul</td> <td>Marshall</td> </tr> <tr> <td>13</td> <td>Ashton</td> <td>Fox</td> <td>Calhoun</td> </tr> <tr> <td>14</td> <td>Garrett</td> <td>John</td> <td>Madison</td> </tr> <tr> <td>15</td> <td>Fredie</td> <td>Winters</td> <td>Washington</td> </tr> </tbody> </table>
</div>css
.table-scroll tbody { position: absolute; overflow-y: scroll; height: 250px;
}
.table-scroll tr { width: 100%; table-layout: fixed; display: inline-table;
}
.table-scroll thead > tr > th { border: none;
} For tables that are full height (the page scrolls, not the table)
Note: I move the whole <thead>...</thead> beause In my case I had two rows (Title and filters)
With JS (jQuery)
$( function() { let marginTop = 0; // Add margin if the page has a top nav-bar let $thead = $('.table-fixed-head').find('thead'); let offset = $thead.first().offset().top - marginTop; let lastPos = 0; $(window).on('scroll', function () { if ( window.scrollY > offset ) { if ( lastPos === 0 ) { // Add a class for styling $thead.addClass('floating-header'); } lastPos = window.scrollY - offset; $thead.css('transform', 'translateY(' + ( lastPos ) + 'px)'); } else if ( lastPos !== 0 ) { lastPos = 0; $thead.removeClass('floating-header'); $thead.css('transform', 'translateY(' + 0 + 'px)'); } });
});CSS (Just for styling)
thead.floating-header>tr>th { background-color: #efefef; }
thead.floating-header>tr:last-child>th { border-bottom: 1px solid #aaa;
} Now that “all” browsers support ES6, I’ve incorporated the various suggestions above into a JavaScript class that takes a table as an argument and makes the body scrollable. It lets the browser’s layout engine determine header and body cell widths, and then makes the column widths match each other.
The height of a table can be set explicitly, or made to fill the remaining part of the browser window, and provides callbacks for events such as viewport resizing and/or details elements opening or closing.
Multi-row header support is available, and is especially effective if the table uses the id/headers attributes for accessibility as specified in the WCAC Guidelines, which is not as onerous a requirement as it might seem.
The code does not depend on any libraries, but plays nicely with them if they are being used. (Tested on pages that use JQuery).
The code and sample usage are available on Github.
This is my implementation for a content scrollable table implemented with just the div element and pure CSS Flexbox styling.
.table { display: flex; flex-direction: column; background-color: lightblue; width: 600px; height: 200px; font-family: "Courier New";
}
.header { display: flex; flex-direction: row; background-color: whitesmoke; padding-right: 10px; font-weight: bold;
}
.row { border-bottom: 1px solid gainsboro;
}
.row>div { width: 25%; text-align: left;
}
.content { height: 100%; display: flex; flex-direction: column; overflow-y: scroll;
}
.entry { display: flex; flex-direction: row; padding-top: 5px;
}
/*
Chrome, Edge, Safari and Opera support the non-standard ::-webkit-scrollbar pseudo element.
We need the scroll bar width set so as to apply same padding in the table header row for alignment.
*/
/* width */
::-webkit-scrollbar { width: 10px;
}
/* Track */
::-webkit-scrollbar-track { background: whitesmoke; border-radius: 2px;
}
/* Handle */
::-webkit-scrollbar-thumb { background: lightgrey; border-radius: 2px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover { background: grey;
}<div> <div> <div>Fruit</div> <div>Price</div> <div>Quantity</div> <div>In Stock</div> </div> <div> <div> <div>Apple</div> <div>$10</div> <div>100</div> <div>Yes</div> </div> <div> <div>Pear</div> <div>$2</div> <div>900</div> <div>No</div> </div> <div> <div>Orange</div> <div>$3</div> <div>123400</div> <div>Yes</div> </div> <div> <div>Mongosteen</div> <div>$80</div> <div>5</div> <div>Yes</div> </div> <div> <div>Durian</div> <div>$120</div> <div>988</div> <div>Yes</div> </div> <div> <div>Apple</div> <div>$10</div> <div>100</div> <div>Yes</div> </div> <div> <div>Pear</div> <div>$2</div> <div>900</div> <div>No</div> </div> <div> <div>Orange</div> <div>$3</div> <div>123400</div> <div>Yes</div> </div> <div> <div>Mongosteen</div> <div>$80</div> <div>5</div> <div>Yes</div> </div> <div> <div>Durian</div> <div>$120</div> <div>988</div> <div>Yes</div> </div> </div>
</div> See the Pen Content Scrollable Table - CSS only by Ruifeng Ma (@maruifeng) on CodePen. For whatever it's worth now: I did post a solution to a sister-thread Table scroll with HTML and CSS which
- takes two tables (one for only header, one for all - layouted by the browser)
- after layouting, adjust the upper (header-only) table to the widths of the lower one
- hide (
visibility, notdisplay) the header of the lower table and make the lower table scrollable w/in a div
The solution is agnostic to any styles / frameworks used - so maybe it's useful here as well...
A long description is in Table scroll with HTML and CSS / the code is also in this pen:
Cleaner Solution (CSS Only)
.table-fixed tbody { display:block; height:85vh; overflow:auto;
}
.table-fixed thead, .table-fixed tbody tr { display:table; width:100%;
}
<table> <thead> <tr align="center"> <th>Col 1</th> <th>Col 2</th> <th>Col 3</th> <th>Col 4</th> </tr> </thead> <tbody> <tr> <td>Content 1</td> <td>Content 1</td> <td>Content 1</td> <td>Content 1</td> </tr> <tr> <td>Longer Content 1</td> <td>Longer Content 1</td> <td>Longer Content 1</td> <td>Longer Content 1</td> </tr> </tbody
</table Used this link, , by Hashem Qolami at the original posts' comments and used display:inline-blocks instead of floats. Fixes borders if the table has the class 'table-bordered' also.
table.scroll { width: 100%; &.table-bordered { td, th { border-top: 0; border-right: 0; } th { border-bottom-width: 1px; } td:first-child, th:first-child { border-right: 0; border-left: 0; } } tbody { height: 200px; overflow-y: auto; overflow-x: hidden; } tbody, thead { display: block; } tr { width: 100%; display: block; } th, td { display: inline-block; } td { height: 46px; //depends on your site }
}Then just add the widths of the td and th
table.table-prep { tr > td.type, tr > th.type{ width: 10%; } tr > td.name, tr > th.name, tr > td.notes, tr > th.notes, tr > td.quantity, tr > th.quantity{ width: 30%; }
} put the table inside the div like this to make scrollable table vertically. change overflow-yto overflow-x to make table scrollable horizontally. just overflow to make table scrollable both horizontal and vertical.
<div> <table> ... </table>
</div> <style>
thead, tbody
{ display: block;
}
tbody
{ overflow: auto; height: 100px;
}
th,td
{ width: 120px;
}
</style>
<table> <thead> <tr> <th>Id</th><th>Name</th><th>Roll</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> <tr> <td>1</td> <td>Shahriar</td> <td>12</td> </tr> </tbody>
</table> All of the six or so CSS solutions assumed a short table. These should have tested with something hundreds of rows long.
based on @Roko C. Buljan answer, if you try to add rowspan, Only the first th will be fixed when you scroll.
.tableFixHead { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
/* Just common table stuff. Really. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th { background:#eee; }<div> <table> <thead> <tr> <th rowspan="3">TH 1</th> <th>TH 2.1</th> <th>TH 3.1</th> </tr> <tr > <th>TH 2.2</th> <th>TH 3.2</th> </tr> <tr > <th>TH 2.3</th> <th>TH 3.3</th> </tr> </thead> <tbody> <tr><td>A1</td><td>A2</td><td>A3</td></tr> <tr><td>B1</td><td>B2</td><td>B3</td></tr> <tr><td>C1</td><td>C2</td><td>C3</td></tr> <tr><td>D1</td><td>D2</td><td>D3</td></tr> <tr><td>E1</td><td>E2</td><td>E3</td></tr> </tbody> </table>
</div>You can add this line if you want try to fixed the rowspan
.tableFixHead thead tr:nth-child(2) th { top: 34px; /* change the number according to your need */
}
.tableFixHead thead tr:nth-child(3) th { top: 68px; /* change the number according to your need */
}for an example you can check this snippet
.tableFixHead { overflow: auto; height: 200px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead thead tr:nth-child(2) th { top: 34px; /* change the number according to your need */
}
.tableFixHead thead tr:nth-child(3) th { top: 68px; /* change the number according to your need */
}
/* Just common table stuff. Really. */
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th { background:#eee; }<div> <table> <thead> <tr> <th rowspan="3">TH 1</th> <th>TH 2.1</th> <th>TH 3.1</th> </tr> <tr > <th>TH 2.2</th> <th>TH 3.2</th> </tr> <tr > <th>TH 2.3</th> <th>TH 3.3</th> </tr> </thead> <tbody> <tr><td>A1</td><td>A2</td><td>A3</td></tr> <tr><td>B1</td><td>B2</td><td>B3</td></tr> <tr><td>C1</td><td>C2</td><td>C3</td></tr> <tr><td>D1</td><td>D2</td><td>D3</td></tr> <tr><td>E1</td><td>E2</td><td>E3</td></tr> </tbody> </table>
</div>above snippet will work if you specify number manually with nth-child(number)
You can place two div where 1st div (Header) will have transparent scroll bar and 2nd div will be have data with visible/auto scroll bar. Sample has angular code snippet for looping through the data.
Below code worked for me -
<div> <div> <div><strong>{{col1}}</strong></div> <div><strong>{{col2}}</strong></div> <div><strong>{{col3}}</strong></div> </div>
</div>
<div> <div> <div ng-repeat="row in rows"> <div>{{row.col1}}</div> <div>{{row.col2}}</div> <div>{{row.col3}}</div> </div> </div>
</div>Additional style to hide header scroll bar -
<style> #transparentScrollbarDiv::-webkit-scrollbar { width: inherit; } /* this targets the default scrollbar (compulsory) */ #transparentScrollbarDiv::-webkit-scrollbar-track { background-color: transparent; } /* the new scrollbar will have a flat appearance with the set background color */ #transparentScrollbarDiv::-webkit-scrollbar-thumb { background-color: transparent; } /* this will style the thumb, ignoring the track */ #transparentScrollbarDiv::-webkit-scrollbar-button { background-color: transparent; } /* optionally, you can style the top and the bottom buttons (left and right for horizontal bars) */ #transparentScrollbarDiv::-webkit-scrollbar-corner { background-color: transparent; } /* if both the vertical and the horizontal bars appear, then perhaps the right bottom corner also needs to be styled */ </style> table { display: block;
}
thead, tbody { display: block;
}
tbody { position: absolute; height: 150px; overflow-y: scroll;
}
td, th { min-width: 100px !important; height: 25px !important; overflow:hidden !important; text-overflow: ellipsis !important; max-width: 100px !important;
}<link href="" rel="stylesheet"/>
<script src=""></script>
<script src=""></script>
<div>
<table> <thead> <tr> <th>Col1</th> <th>Col2</th> <th>Username</th> <th>Password</th> <th>First Name</th> <th>Last Name</th> <th>Col16</th> <th>Col7</th> <th>Col8</th> <th>Col9</th> <th>Col10</th> <th>Col11</th> <th>Col12</th> <th>Col13</th> <th>Col14</th> <th>Col15</th> <th>Col16</th> <th>Col17</th> <th>Col18</th> </tr> </thead> <tbody> </tbody> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> <tr> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> <td>Long Value</td> <td>Title</td> </tr> </table>
</div>`enter code here` An easy way without fixed width:
.myTable tbody{ display:block; overflow:auto; height:200px; width:100%;
}
.myTable thead tr{ display:block;
}Now, on onLoad, to adjust th widths, just add this jquery script:
$.each($('.myTable tbody tr:nth(0) td'), function(k,v) { $('.myTable thead th:nth('+k+')').css('width', $(v).css('width'));
});