440

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="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">

<table class="table table-striped">
    <thead>
    <tr>
        <th>Make</th>
        <th>Model</th>
        <th>Color</th>
        <th>Year</th>
    </tr>
    </thead>
    <tbody style="height: 10px !important; overflow: scroll; ">
    <tr>
        <td class="filterable-cell">111 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>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
giulio
  • 5,865
  • 3
  • 22
  • 20
  • 5
    Does this help? http://stackoverflow.com/a/17380697/1725764 – Hashem Qolami Jan 16 '14 at 17:20
  • 1
    This definitely helped me out. Here the solution http://jsfiddle.net/T9Bhm/7/ Thank you – giulio Jan 16 '14 at 23:30
  • 1
    Doesn't correctly handle table-striped attribute when td heights dont match. http://jsfiddle.net/T9Bhm/4755/ See the added td { overflow-wrap: break-word; } that I added – John Zabroski Jun 20 '16 at 20:24
  • 2
    I think better and older SO thread is marked as duplicate of this :p https://stackoverflow.com/questions/17067294/html-table-with-100-width-with-vertical-scroll-inside-tbody – DevLoverUmar Jul 15 '20 at 10:25
  • 1
    Does this answer your question? [How do I create an HTML table with a fixed/frozen left column and a scrollable body?](https://stackoverflow.com/questions/1312236/how-do-i-create-an-html-table-with-a-fixed-frozen-left-column-and-a-scrollable-b) – Garry Nov 19 '20 at 15:17
  • 1
    In bootstrap 4 the sticky-top utility class sets position: sticky; top: 0; ensuring the header never scrolls off the top of the screen (I'm not sure if this works in Bootstrap 3). Just apply it to all your th tags. Not sure if this is your exact scenario and I have only tested in Chrome. – jsaven Mar 07 '21 at 02:58

30 Answers30

583

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 class="tableFixHead">
  <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 class="tableFixHead">
  <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 class="tableFixHead">
  <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 stackoverflow.com/Why is 'position: sticky' not working?


Fixed table head - using JavaScript

For ancient browsers, you can use a bit of JS and translateY the th elements

// Fix table head example:

function tableFixHead(evt) {
  const el = evt.currentTarget,
    sT = el.scrollTop;
  el.querySelectorAll("thead th").forEach(th =>
    th.style.transform = `translateY(${sT}px)`
  );
}

document.querySelectorAll(".tableFixHead").forEach(el =>
  el.addEventListener("scroll", tableFixHead)
);
.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 class="tableFixHead">
  <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>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • @Roko thank you so much for providing such a clean solution, it works like charm in chrome. Unfortunately, this is not working in IE11. Is there any tweak to make it work in IE11. – A P Nov 23 '18 at 19:37
  • 1
    @AP for IE11 (since it does not understands *sticky*) you could use the second example that uses `translateY` (and the jQuery code - if you don't compile your ES6 js code). **It'll work**, but will look a bit *"jumpish"*. But that's OK. If one wants to use outdated browsers than such person/company will never complain about choppy things - as far as he's able to see the content and have a decent interaction with your website. Interesting read ahead: https://nealbuerger.com/2018/01/the-end-of-life-of-internet-explorer-11/ – Roko C. Buljan Nov 24 '18 at 15:55
  • 3
    Does not work when there is more than one row in the table header. When scrolling the 2nd header line jumps up and lays over the first because they both have "top:0". Tinkered around with making the entire sticky but could not make it work. Any ideas for multi-line headers (with colspans to boot)? – user3191192 May 29 '19 at 18:03
  • Specifically I was referring to the CSS solution. The JS solution works better and both header rows stick. But they both have a minor visual flaw, at least on Chrome 74 - the header border lines are not drawn and rows that scroll behind it become a bit visible. Likewise the top table border also does not get drawn and a pixel of the scrolled table data becomes visible at the top edge of the table. – user3191192 May 29 '19 at 19:34
  • @user3191192 (using JS example) on desktop, Chrome 74 works great for me, only on mobile there's a small hop. Regarding TH `border`, yes, it cannot be drawn by the browser. See my edited answer for a solution using `box-shadow`. – Roko C. Buljan May 29 '19 at 21:30
  • 2
    If the top solution of `{ position: sticky; top: 0; }` somehow does not work in your code - check the `overflow-x` and `overflow-y` css properties of containing elements, especially `table`, `thead`, `tr`. Overflows, surprisingly both of them, must not be set. You may need to explicitly `unset` them. More info - https://stackoverflow.com/a/54806576/882936 – Ivan Koshelev Sep 28 '19 at 19:46
  • 1
    position: sticky is widely underused and a bit counter intuitive, especially since you need the top: 0 as well. But this is actually a perfect case to show how to use position sticky purposefully! – ViBoNaCci Apr 16 '20 at 13:25
  • How do you add a horizontal scroll to the css only option? – ibexy Apr 07 '21 at 21:44
  • @ibexy edited my answer to account for sticky TH columns. Here's a demo: https://jsfiddle.net/RokoCB/863s5mt4/ – Roko C. Buljan Apr 08 '21 at 04:01
  • this not working for `rowspan` header @RokoC.Buljan – AdityaDees Feb 28 '22 at 06:51
  • It is working: https://jsfiddle.net/L69ho31p/ @AdityaDees – Roko C. Buljan Feb 28 '22 at 07:05
  • i've post the answer about what I mean https://stackoverflow.com/a/71291701/7590238 – AdityaDees Feb 28 '22 at 07:22
  • Doesn't work on FireFox :-( – Siyon DP Dec 27 '22 at 14:06
  • t, dbol, just basic stuff! – filippo Mar 28 '23 at 18:35
  • stuff basic, dbol, t! just @filippo – Roko C. Buljan Mar 28 '23 at 19:46
100

For a pure CSS approach you'd need a container with overflow-y: auto; and decide how to hide scrolled/overflown rows:

  1. Overlay by a non-transparent sticky header (position: sticky; top: 0; z-index: 1;), like in the @RokoCBuljan's answer.
  2. Toggle visibility of the rows (by setting tr:after properties 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 class="header-fixed">
 <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.

Alex Klaus
  • 8,168
  • 8
  • 71
  • 87
  • 3
    after following all the different complex solutions, I find that this is the best solution. However the vertical scroll bar at has some width(which is obvious), but due to it there is the slight mismatch alignment between headers and the rows. I have 7 columns, so I have set width to 14.28% after calculating using the mention formula – Chetan Feb 25 '15 at 12:48
  • 1
    How would I assign a certain width to each column? – user1807271 May 27 '15 at 14:28
  • @user1807271 you can assign each column's width via JS, or create a per-column class (say "col1", "col2" and so on) with the width you need and assign class "col1" to all the cells on the first column, "col2" con the second one, etc.. – Luke Sep 01 '15 at 13:33
  • 4
    I suggest you add `.header-fixed > thead > tr > th{white-space: nowrap;}` as well. If headers start wrapping it messes things up – Omar Wagih Apr 06 '16 at 10:16
  • Would you mind making a [mcve] using the `[<>]` snippet editor? – mplungjan Apr 11 '22 at 09:36
  • @mplungjan, added a code snippet. – Alex Klaus Apr 12 '22 at 04:32
93

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>

Link to jsfiddle

Philipp Kief
  • 8,172
  • 5
  • 33
  • 43
giulio
  • 5,865
  • 3
  • 22
  • 20
  • 34
    Doesn't work in IE9 (and probably others); requires fixed-width columns (and there are much simpler, more cross-browser solutions if you're going to use fixed-width columns); breaks badly if the content exceeds the fixed width; doesn't actually line up the headers and the columns (you can see it in [the fiddle linked above](http://jsfiddle.net/T9Bhm/7); it's clearer in [this version](http://jsfiddle.net/T9Bhm/489) that gives them a simple border) although that could probably be fixed; the scrollbar isn't quite lined up with the body of the table... – T.J. Crowder Jun 21 '14 at 12:22
  • add a visible border to td and th and you will see that td's width does not match th's width – Apolo Oct 28 '14 at 13:26
  • 5
    The problem with this solution is that it requires a fixed height. That is not going to work where responsive design come in. I have been working on a solution that uses position: fixed, which solves the scrolling issue, but messes up the row widths. – Jeffrey A. Gochin Sep 07 '15 at 00:05
  • You can manually fix the td width not matching the th width by seperating out the width % declaration for the td & th and just set the th to be slightly more narrow. You will have do it custom every time but this isn't something that should be used that often. – Ben Hoffman Jul 28 '16 at 15:51
  • Had issue with this solution on the header row: The header role only capable of having not more than 4 column, and new row will summon if you're having more than 4 columns.*Need to adjust the width % to your corresponding row number* – Jason Lim Ji Chen Mar 11 '21 at 03:19
63

Update

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;
}
Rosdi Kasim
  • 24,267
  • 23
  • 130
  • 154
  • 9
    Note that the stickyTableHeaders plugin will only find html that is in the page when browser loaded it initially, it won't pick up dynamically generated content – Rob Sedgwick Sep 02 '15 at 11:38
  • 3
    @RobSedgwick, I haven't this this, but it should still work. As long as you initialize stickyTableHeadres _AFTER_ the table is generated. Meaning you can't initialize it in the head but instead initialize it right after the dynamically generated table is completed. – Rosdi Kasim Sep 03 '15 at 21:38
  • 1
    Fantastic solution. Much appreciated. If anyone is having problems making the background opaque, I had to use `.tableFloatingHeaderOriginal { //css }`. – Matt Inamdar Apr 29 '16 at 19:19
  • This plugin did not work for me, as occasionally the head floats out of the table and then floats back, which makes it look like st is broken. – Mati Dec 01 '16 at 10:37
  • How can we adjust vertical scroll bar space. Can we do it by using css. – M2012 May 22 '17 at 12:54
40

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 : https://www.codeply.com/p/nkaQFc713a

BENARD Patrick
  • 30,363
  • 16
  • 99
  • 105
40

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;
}
St John
  • 431
  • 4
  • 5
  • 3
    I skipped over this one for the much higher rated answer by @Roko C. Buljan below only to find that answer didn't work when the header had multiple rows. This solution works great. Thank you so much!!! – shao.lo Sep 23 '20 at 01:22
  • Dunno if it's because my table is in a nested grid layout or the fact that the table is really wide, but this produces the most bizarre rendering on my table - it's like each column is placed a couple pixels offset to the right so each column nearly overwrites the previous one resulting in a very malfunctioned look – Michael Dec 07 '20 at 17:49
  • This doesn't work if you want to use `` to manage width of columns of your table - all your settings are ignored. – izogfif Dec 10 '20 at 14:28
  • got around the colgroup issue with `.table-scroll th:nth-child(1), .table-scroll td:nth-child(1) { width: 3%; }` for n number of columns – Tom Shelley Sep 17 '21 at 18:30
  • 3
    Unlike the top solution, this requires a table height set to lower than the page and an extra inner scrollbar will also appear. Sometimes this is what you want (eg for a fixed-size inner table) but otherwise it's less than ideal. – Steve Horvath May 03 '22 at 04:50
32

It 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; }
Mert Simsek
  • 700
  • 7
  • 12
25

table {
    overflow-y: auto;
    height: 50vh;     /* !!!  HEIGHT MUST BE IN [ vh ] !!! */
}

thead th {
    background-color: white;
    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]

TomDom
  • 258
  • 3
  • 7
  • 1
    Worked like a charm! Thanks. It starts floating when the header goes offscreen. Just what I needed. I did set a background color so its not mixing with the table content and a z-index:2, so its in front – Mark Oct 05 '21 at 11:39
  • Nice. Top it off with styling the TH elements: `th { background-color: white; opacity: 1; z-index: 100; }` – dat Nov 18 '22 at 21:24
  • I added a background in the header. I'm not sure if that was the right thing to do because now you can't see the rows scrolling under the header. – TomDom Dec 06 '22 at 10:43
16

Late 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;
}
Vee
  • 428
  • 4
  • 11
12

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 id="example" class="display" 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
});
bluish
  • 26,356
  • 27
  • 122
  • 180
KD2ND
  • 321
  • 2
  • 15
  • i want both the first column and the header to be fixed which is not possible as per the data tables compatibility chart – PirateApp Jul 06 '18 at 15:30
9

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>
voddy
  • 950
  • 1
  • 11
  • 21
  • Upvoted with a note that the outer element is not needed (just set `block` to table display, anonymous table wrapper [will be created](https://drafts.csswg.org/css2/tables.html)), fixed table layout is not needed and `sticky` [can't be set](https://caniuse.com/css-sticky) on thead and tr in today's browsers (would you add this info to your answer to save others troubles implementing it?) – Jan Turoň Mar 10 '21 at 14:01
5

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 class="ea_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>

jsfiddle

Ersel Aktas
  • 111
  • 1
  • 4
  • This solution suffers from most other examples. The table headers do not line up vertically with the body content – Aaron Hudon Sep 15 '22 at 21:11
4

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"

  • display:block was necessary but then I also had to put float:left and a proper width for all the cell. The solution I have posted is now working. – giulio Jan 16 '14 at 23:32
  • I did display:block on the tbody and thead and they align perfectly. Block is the key for the scrolling and height to work. Thanks. – blissweb Oct 26 '20 at 07:14
4

I 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.

Bob Jordan
  • 3,049
  • 2
  • 34
  • 41
4

I used the floatThead jQuery plugin (https://mkoryak.github.io/floatThead/#intro)

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 class="table-responsive">
   <table id="myTable" class="table table-striped">
        <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.

Drew
  • 4,215
  • 3
  • 26
  • 40
2

First 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 class="col-xs-8 col-xs-offset-2 well">
    <table class="table table-scroll table-striped">
        <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;
}
core114
  • 5,155
  • 16
  • 92
  • 189
2

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;
}
antman3351
  • 436
  • 3
  • 8
2

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.

2

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 class="table">
  <div class="header row">
    <div>Fruit</div>
    <div>Price</div>
    <div>Quantity</div>
    <div>In Stock</div>
  </div>
  <div class="content">
    <div class="entry row">
      <div>Apple</div>
      <div>$10</div>
      <div>100</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Pear</div>
      <div>$2</div>
      <div>900</div>
      <div>No</div>
    </div>
    <div class="entry row">
      <div>Orange</div>
      <div>$3</div>
      <div>123400</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Mongosteen</div>
      <div>$80</div>
      <div>5</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Durian</div>
      <div>$120</div>
      <div>988</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Apple</div>
      <div>$10</div>
      <div>100</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Pear</div>
      <div>$2</div>
      <div>900</div>
      <div>No</div>
    </div>
    <div class="entry row">
      <div>Orange</div>
      <div>$3</div>
      <div>123400</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <div>Mongosteen</div>
      <div>$80</div>
      <div>5</div>
      <div>Yes</div>
    </div>
    <div class="entry row">
      <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.
Ruifeng Ma
  • 2,399
  • 1
  • 22
  • 40
1

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, not display) 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: https://codepen.io/sebredhh/pen/QmJvKy

1

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 class="table table-striped table-fixed">
    <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
1

Just found a cool solution using grid! This is what I used and it worked out perfectly:

tbody tr, thead tr {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr; // or whatever division you wanna do
}

tbody {
  height: 300px; // or whatever height
  width: 100%;
  overflow-y: auto;
  display: block;
}

Demo: https://codesandbox.io/s/table-with-inner-body-scroll-hggq7x

avivyar
  • 31
  • 4
0

Used this link, stackoverflow.com/a/17380697/1725764, 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%;
  }
}
Community
  • 1
  • 1
juppee
  • 47
  • 3
0

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 style="overflow-y: scroll;"> 
    <table>
    ...
    </table>
</div>
0
<style>

thead, tbody
{
    display: block;
}

tbody 
{
   overflow: auto;
   height: 100px;
}

th,td
{
    width: 120px;
}

</style>

<table class="table table-bordered table-striped">
    <thead style="background-color:lightgreen">
        <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>
Md Shahriar
  • 2,072
  • 22
  • 11
0

All of the six or so CSS solutions assumed a short table. These should have tested with something hundreds of rows long.

Greg
  • 2,359
  • 5
  • 22
  • 35
0

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 class="tableFixHead">
  <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 class="tableFixHead">
  <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)

AdityaDees
  • 1,022
  • 18
  • 39
-1

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 id="transparentScrollbarDiv" class="container-fluid" style="overflow-y: scroll;">
    <div class="row">
        <div class="col-lg-3 col-xs-3"><strong>{{col1}}</strong></div>
        <div class="col-lg-6 col-xs-6"><strong>{{col2}}</strong></div>
        <div class="col-lg-3 col-xs-3"><strong>{{col3}}</strong></div>
    </div>
</div>
<div class="container-fluid" style="height: 150px; overflow-y: auto">
    <div>
        <div class="row" ng-repeat="row in rows">
            <div class="col-lg-3 col-xs-3">{{row.col1}}</div>
            <div class="col-lg-6 col-xs-6">{{row.col2}}</div>
            <div class="col-lg-3 col-xs-3">{{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>
-1

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="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet"/>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>

<div class="container" style="position:fixed;height:180px;overflow-x:scroll;overflow-y:hidden">


<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`
Siddhartha
  • 1,473
  • 14
  • 10
-1

An easy way without fixed width:

.myTable tbody{
  display:block;
  overflow:auto;
  height:200px;
  width:100%;
}
.myTable thead tr{
  display:block;
}

Source

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'));
});
mauretto
  • 3,183
  • 3
  • 27
  • 28