[twitter-bootstrap] Twitter bootstrap scrollable table

I would like to have a table on my website. The problem is that this table will have about 400 lines. How can I limit the table's height, and apply scrollbar to it? This is my code:

<div class="span3">
  <h2>Achievements left</h2>
<table class="table table-striped">
  <thead>
    <tr>
      <th>#</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Something</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Something</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Something</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Something</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Something</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Something</td>
    </tr>
  </tbody>
</table>

  <p><a class="btn" href="#">View details &raquo;</a></p>
</div>

I tried to apply max-height and fixed height to table, but it doesn't work.

This question is related to twitter-bootstrap

The answer is


I added .table-responsive to the table and it worked. From the docs

Create responsive tables by wrapping any .table with .table-responsive{-sm|-md|-lg|-xl}, making the table scroll horizontally at each max-width breakpoint of up to (but not including) 576px, 768px, 992px, and 1120px, respectively.


This example shows how to have sticky headers when using Bootstrap 4 table styling.

_x000D_
_x000D_
.table-scrollable {
    /* set the height to enable overflow of the table */
    max-height: 200px;
    
    overflow-x: auto;
    overflow-y: auto;
    scrollbar-width: thin;
}

.table-scrollable thead th {
    border: none;
}

.table-scrollable thead th {
    /* Set header to stick to the top of the container. */
    position: sticky; 
    top: 0px;
    
    /* This is needed otherwise the sticky header will be transparent 
    */
    background-color: white;

    /* Because bootstrap adds `border-collapse: collapse` to the
     * table, the header boarders aren't sticky.
     * So, we need to make some adjustments to cover up the actual
     * header borders and created fake borders instead
     */
    margin-top: -1px;
    margin-bottom: -1px;

    /* This is our fake border (see above comment) */
    box-shadow: inset 0 1px 0 #dee2e6,
                inset 0 -1px 0 #dee2e6;
}
_x000D_
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

<!-- jQuery and JS bundle w/ Popper.js -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>


<div class="dashboard-container card">
  <div class="card-body">
    <div class="table-scrollable">
      <table class="table table-hover table-sortable">
          <thead>
              <tr>
                  <th data-sort-type="text">Course</th>
                  <th data-sort-type="numeric">In Progress</th>
                  <th data-sort-type="numeric">Not Started</th>
                  <th data-sort-type="numeric">Passed</th>
                  <th data-sort-type="numeric">Failed</th>
              </tr>
          </thead>
          <tbody>
            <tr>
              <td>How to be good at stuff</td>
              <td>0</td>
              <td>1000</td>
              <td>0</td>
              <td>0</td>
            </tr>
            <tr>
              <td>Quantum physics for artists</td>
              <td>200</td>
              <td>6</td>
              <td>66</td>
              <td>66</td>
            </tr>
            <tr>
              <td>The best way to skin a cat</td>
              <td>34</td>
              <td>16</td>
              <td>200</td>
              <td>7</td>
            </tr>
            <tr>
              <td>Human cookbook</td>
              <td>4</td>
              <td>7</td>
              <td>4</td>
              <td>50</td>
            </tr>
            <tr>
              <td>Aristocracy rules</td>
              <td>100</td>
              <td>3</td>
              <td>6</td>
              <td>18</td>
            </tr>
          </tbody>
      </table>
    </div>
  </div>
</div>
_x000D_
_x000D_
_x000D_


All above solutions make table header scroll too... If you want to scroll tbody only then apply this:

tbody {
    height: 100px !important;
    overflow: scroll;
    display:block;
}

.span3 {  
    height: 100px !important;
    overflow: scroll;
}?

You'll want to wrap it in it's own div or give that span3 an id of it's own so you don't affect your whole layout.

Here's a fiddle: http://jsfiddle.net/zm6rf/


None of these answers worked satisfactorily for me. They either didn't fix the table heading row in place or they required fixed column widths to work, and even then tended to have the heading row and body rows misaligned in various browsers.

I recommend biting the bullet and using a proper grid control like jsGrid or my personal favorite, SlickGrid. It obviously introduces a dependency but if you want your tables to behave like real grids with cross-browser support, this will save you pulling your hair out. It also gives you the option of sorting and resizing columns plus tons of other features if you want them.


I recently had to do this with bootstrap and angularjs, I created an angularjs directive which solves the problem, you can see a working example and details on this blog post.

You use it just by adding a fixed-header attribute to the table tag:

<table class="table table-bordered" fixed-header>
...
</table>

If you're not using angularjs you could tweak the directive's javascript and use it directly instead.


Figured I would post on here since I could not get any of the above to work with Bootstrap 4. I found this online and worked perfectly for me...

table
{
    width: 100%;
    display: block;
    border:solid black 1px;
}

thead
{
    display: inline-block;
    width: 100%;
    height: 20px;
}

tbody
{
    height: 200px;
    display: inline-block;
    width: 100%;
    overflow: auto;
}

th, td
{
    width: 100px;
    border:solid 1px black;
    text-align:center;
}

I have also attached the working jsfindle.

https://jsfiddle.net/jgngg28t/

Hope it helps someone else.


Put the table in a div and give that div the class pre-scrollable.


CSS

.achievements-wrapper { height: 300px; overflow: auto; }

HTML

<div class="span3 achievements-wrapper">
    <h2>Achievements left</h2>
    <table class="table table-striped">
    ...
    </table>
</div>

This is may not a solution for large row count. I simply use duplication table trick to get the thing done for small row count table while it can keep flex column width, you can try this fiddle.

(The fixed width column is the method I use for large row count table which require only the header row duplication)

Sample Code:

<div style="height:30px;overflow:hidden;margin-right:15px;">
   <table class="table">
       <thead>
           <tr>
                <th>Col 1</th>
                <th>Col 2</th>
                <th>Col 3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Cel 1,1</td>
                <td>Cel 1,2</td>
                <td>Cel 1,3</td>
            </tr>
            <tr>
                <td>Cel 2,1</td>
                <td>Cel 2,2</td>
                <td>Cel 2,3</td>
            </tr>
            <tr>
                <td>Cel 3,1</td>
                <td>Cel 3,2</td>
                <td>Cel 3,3</td>
            </tr>


        </tbody>
    </table>
</div>
<div style="height:100px;overflow-y:scroll;;">
    <table class="table">
        <thead>

        </thead>
        <tbody>
            <tr>
                <td>Cel 1,1</td>
                <td>Cel 1,2</td>
                <td>Cel 1,3</td>
            </tr>
            <tr>
                <td>Cel 2,1</td>
                <td>Cel 2,2</td>
                <td>Cel 2,3</td>
            </tr>
            <tr>
                <td>Cel 3,1</td>
                <td>Cel 3,2</td>
                <td>Cel 3,3</td>
            </tr>
             <tr style="color:white">
                <th>Col 1</th>
                <th>Col 2</th>
                <th>Col 3</th>
            </tr>
        </tbody>
    </table>
</div>

Re Jonathan Wood's suggestion. I don't have the option of wrapping tables with a new div as i'm using a CMS. Using JQuery here's what i did:

$( "table" ).wrap( "<div class='table-overflow'></div>" );

This wraps table elements with a new div with the class "table-overflow".

You can then simply add the following definition in your css file:

.table-overflow { overflow: auto; }     

I recently had a similar problem and ended up fixing it using a mixture of different solutions.

The first and most simple one was to use two tables, one for the headers and one for the body. This works but the headers and the body columns are not aligned. And, since I wanted to use the auto-size that comes with twitter bootstrap tables I ended up creating a Javascript function that changes the headers when: the body is rendered; the windows is resized; the data in the column changes, etc.

Here is some of the code I used:

        <table class="table table-striped table-hover" style="margin-bottom: 0px;">
        <thead>
            <tr>
                <th data-sort="id">Header 1</i></th>
                <th data-sort="guide">Header 2</th>
                <th data-sort="origin">Header 3</th>
                <th data-sort="supplier">Header 4</th>
            </tr>
        </thead>
    </table>
    <div class="bodycontainer scrollable">
        <table class="table table-hover table-striped table-scrollable">
            <tbody id="rows"></tbody>
        </table>
    </div>

The headers and the body are divided in two separate tables. One of them is inside a DIV with the necessary style to generate the vertical scrollbars. Here is the CSS I used:

.bodycontainer {
  //height: 200px
  width: 100%;
  margin: 0;
}

.table-scrollable {
  margin: 0px;
  padding: 0px;
}

I commented the height here because I a wanted the table to reach the bottom of the page, whatever the page height might be.

The data-sort attributes I used in the headers are also used in every td. This way I could get the width and the padding of every td and the width of the row. Using the data-sort attributes I set using CSS the padding and width of each header accordingly and of the header row which is always bigger since it doesn´t have a scrollbar. Here is the function using coffeescript:

fixHeaders: =>
  for header, i in @headers
    tdpadding = parseInt(@$("td[data-sort=#{header}]").css('padding'))
    tdwidth = parseInt(@$("td[data-sort=#{header}]").css('width'))
    @$("th[data-sort=#{header}]").css('padding', tdpadding)
    @$("th[data-sort=#{header}]").css('width', tdwidth)
    if (i+1) == @headers.length
      trwidth = @$("td[data-sort=#{header}]").parent().css('width')
      @$("th[data-sort=#{header}]").parent().parent().parent().css('width', trwidth)
      @$('.bodycontainer').css('height', window.innerHeight - ($('html').outerHeight() -@$('.bodycontainer').outerHeight() ) ) unless @collection.length == 0

Here I assume that you have an array of the headers called @headers.

It is not pretty but it works. Hope it helps someone.


put the table inside the div 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>

I had the same issue and used a combination of the above solutions (and added a twist of my own). Note that I had to specify column widths to keep them consistent between header and body.

In my solution, the header and footer stay fixed while the body scrolls.

<div class="table-responsive">
    <table class="table table-striped table-hover table-condensed">
        <thead>
            <tr>
                <th width="25%">First Name</th>
                <th width="13%">Last Name</th>
                <th width="25%" class="text-center">Address</th>
                <th width="25%" class="text-center">City</th>
                <th width="4%" class="text-center">State</th>
                <th width="8%" class="text-center">Zip</th>
            </tr>
        </thead>
    </table>
    <div class="bodycontainer scrollable">
        <table class="table table-hover table-striped table-condensed table-scrollable">
            <tbody>
                <!-- add rows here, specifying same widths as in header, at least on one row -->
            </tbody>
        </table>
    </div>
    <table class="table table-hover table-striped table-condensed">
        <tfoot>
            <!-- add your footer here... -->
        </tfoot>
    </table>
</div>

And then just applied the following CSS:

.bodycontainer { max-height: 450px; width: 100%; margin: 0; overflow-y: auto; }
.table-scrollable { margin: 0; padding: 0; }

I hope this helps someone else.


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: -moz-groupbox;    // Firefox Bad Effect
}
tbody{
  overflow-y: scroll;      
  height: 200px;            //  <-- Select the height of the body
  width: 100%;
  position: absolute;
}

Bootply : http://www.bootply.com/AgI8LpDugl