68

I am trying to design a page where there are some tables. It seems that styling tables is much more painful than it ought to be.

The problem is the following: The tables should have a fixed height and display either white space at the bottom (when there is too little content) or a vertical scrollbar (when there is too much). Add to this that the tables have a header which should not scroll.

As far as I know, the thead not scrolling is the default behaviour for tables. And a stretching tfoot could serve well for the purpose of filling with white space. Sadly, it seems that every constraint I can put on the table height is cheerfully ignored. I have tried

table {
    height: 600px;
    overflow: scroll;
}

I have tried with max-height. I have tried to position the table absolutely and give both the top and bottom coordinates. I have tried to manually edit the height in Firebug to see if it was a problem with CSS specificity. I have tried to set the height on the tbody too. Fact is, the table always stays exactly the same height as its content, regardless of my efforts.

Of course I could fake a table with a div structure, but it actually is a table, and I fear using divs I may run into an issue where some columns may not be properly aligned.

How am I supposed to give a table a height?

Benjamin
  • 234
  • 1
  • 10
Andrea
  • 20,253
  • 23
  • 114
  • 183
  • See also [I need my html table's body to scroll and its head to stay put](http://stackoverflow.com/questions/130564/i-need-my-html-tables-body-to-scroll-and-its-head-to-stay-put) and [my answer](http://stackoverflow.com/a/13131562/1269037), which I believe solves the problem. – Dan Dascalescu Nov 08 '12 at 04:35
  • To others looking for an AWESOME answer to this question, check out Sibi John's answer below: https://stackoverflow.com/a/42112251/1735836 – Patricia Feb 20 '18 at 18:11
  • HTML sucks, there are only workaround. For this case, a viable workaround is to enhost the table into a DIV and setting the height-overflow attributes to it. Add `padding: 1px` to avoid yet another scroll bug – AgentFire Mar 10 '20 at 07:46

12 Answers12

28

NOTE this answer is now incorrect. I may get back to it at a later time.

As others have pointed out, you can't set the height of a table unless you set its display to block, but then you get a scrolling header. So what you're looking for is to set the height and display:block on the tbody alone:

<table style="border: 1px solid red">
    <thead>
        <tr>
            <td>Header stays put, no scrolling</td>
        </tr>
    </thead>
    <tbody style="display: block; border: 1px solid green; height: 30px; overflow-y: scroll">
        <tr>
            <td>cell 1/1</td>
            <td>cell 1/2</td>
        </tr>
        <tr>
            <td>cell 2/1</td>
            <td>cell 2/2</td>
        </tr>
        <tr>
            <td>cell 3/1</td>
            <td>cell 3/2</td>
        </tr>
    </tbody>
</table>

Here's the fiddle.

Community
  • 1
  • 1
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
  • 1
    Thank you, tour solution seems to work exactly as I wanted. I do not know why someone downvoted your answer – Andrea Nov 08 '12 at 08:14
  • 91
    Unfortunately, this causes the body's columns to no longer align with the header's columns: http://jsfiddle.net/JPGEk/15/ – Brandon Apr 19 '13 at 00:45
  • 7
    Hate to downvote, but as @Brandon Mintern mentioned, this answer is incorrect – Arek Bal Mar 02 '14 at 19:45
  • 1
    @user1749204: thanks, I've added a comment at the top. Would downvote it myself if I could. – Dan Dascalescu Mar 03 '14 at 02:34
  • Don't state that this is incorrect and you might improve it later, delete the answer instead. Then, if you decide to revisit it, you can always undelete it. – Jasper Sep 26 '14 at 12:44
  • 4
    Please don't delete it, the answer is still partially correct, I was able to use this code and align the header manually with display block and a defined px width. – Nick Sweeting Jul 10 '15 at 19:08
  • 1
    Answer is a little correct but at this point displaying a table as a block destroys the column and row semantics. You should wrap the table in a div and set the max height and overflow-y settings on that div. Not sure how to make the table headers sticky though. – bcstryker Dec 17 '21 at 05:45
21
  • Set display: block; for the table
  • Set position: sticky; top: 0; for the header row
<table style="display: block; height: 100px; overflow: auto;">
  <thead>
    <tr>
      <td style="position: sticky; top: 0;">Header stays put</td>
      <td style="position: sticky; top: 0;">Layout aligned</td>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>foo1</td>
      <td>Header stays put</td>
    </tr>
    <tr>
      <td>foo2</td>
      <td>Header stays put</td>
    </tr>
  </tbody>
</table>

https://jsfiddle.net/0zxk18fp/

Tested on Chrome, Firefox, Safari, Edge

user1124809
  • 445
  • 3
  • 8
  • 1
    This seems like by far the best and simplest. I had no issues other than I also had to add z-index to the table header to prevent table contents from overlapping it when scrolling. – MDave Aug 05 '20 at 17:27
  • 1
    @MDave can you share your code? i'm playng with the z-index and it's not helping me – avi software Sep 01 '20 at 08:02
  • 1
    I just added `background-color: white;` to the td style. – alayor Apr 22 '21 at 15:14
  • 1
    The width of thead and tbody don't occupy the remaining width of the table unless the content is big enough. Otherwise, this is the best solution I've seen yet that doesn't require a separate table and some Javascript. – user0000001 Aug 25 '21 at 12:45
8

Add display:block; to the table's css. (in other words.. tell the table to act like a block element rather than a table.)

fiddle here

Scott
  • 21,475
  • 8
  • 43
  • 55
  • 1
    Thank you. This seems to work partially. The only problem that remains is that now the `thead` scrolls together with the table, while it should stay fixed. I am pretty sure I have seen examples where the body of the table scrolls, but I cannot reproduce it. :-( – Andrea Jan 23 '12 at 11:06
  • 1
    This accepted answer only solved the problem partially. For a complete solution, please see [this answer](http://stackoverflow.com/a/13131562/1269037) – Dan Dascalescu Oct 30 '12 at 00:23
5

You can do this by using the following css.

.scroll-thead{
    width: 100%;
    display: inline-table;
}

.scroll-tbody-y
{
    display: block;
    overflow-y: scroll;
}

.table-body{
    height: /*fix height here*/;
}

Following is the HTML.

<table>
 <thead class="scroll-thead">
          <tr> 
           <th>Key</th>
           <th>Value</th>
          </tr> 
         </thead>
         <tbody class="scroll-tbody-y table-body">
          <tr> 
          <td>Blah</td> 
          <td>Blah</td> 
          </tr> 
</tbody>
</table>

JSFiddle

Sibi John
  • 475
  • 6
  • 22
  • your column headers don't stay inline with your content. IE, make your "keys" header say "keys keys keys keys" instead of just "keys" and they won't line up. – rbrc May 01 '18 at 20:49
4

A simple workaround that is available in most of the cases it to wrap the table in a div and then give a max-height to that div:

.scrollable-wrapper {
  max-height: 400px;
  overflow: auto;
}

/* Add also the following code if sticky header is wanted */
.scrollable-wrapper table thead th {
  background: #afa;
  position: sticky;
  top: 0;
  box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4);
}

<div class="scrollable-wrapper">
  <table>
    <thead>
      <tr>
        <th>Id</th>
        <th>Text</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>
</div>

Codepen: https://codepen.io/Conejoo/pen/NWpjmYw

Aldo Canepa
  • 1,791
  • 2
  • 16
  • 16
3

I had a coworker ask how to do this today, and this is what I came up with. I don't love it but it is a way to do it without js and have headers respected. The main drawback however is you lose some semantics due to not having a true table header anymore.

Basically I wrap a table within a table, and use a div as the scroll container by giving it a max-height. Since I wrap the table in a parent table "colspanning" the fake header rows it appears as if the table respects them, but in reality the child table just has the same number of rows.

One small issue due to the scroll bar taking up space the child table column widths wont match up exactly.

Live Demo

Markup

<table class="table-container">
    <tbody>
        <tr>
            <td>header col 1</td>
            <td>header col 2</td>
        </tr>
        <tr>
            <td colspan="2">
                <div class="scroll-container">
                    <table>
                        <tr>
                            <td>entry1</td>
                            <td>entry1</td>
                        </tr>
                       ........ all your entries
                    </table>
                </div>
            </td>
        </tr>
    </tbody>
</table>

CSS

.table-container {
    border:1px solid #ccc;
    border-radius: 3px;
    width:50%;
}
.table-container table {
    width: 100%;
}
.scroll-container{
    max-height: 150px;
    overflow-y: scroll;
}
Loktar
  • 34,764
  • 7
  • 90
  • 104
  • 3
    This solution does not work properly. Columns of header are not aligned with the body columns at all. In the provided example it looks OK just by coincidence, because header text of each column has equal length and so do body cell contents. It's not only matter of scroll. Please see this [forked fiddle](http://jsfiddle.net/dsgj4z43/) where I've only modified text in header and body. – TMG May 06 '15 at 09:27
  • @Loktar - Hardcoding columns width is good if it contains static content that you know up front, but with dynamic content I would rather like the columns to adjust proportionally to content, like in a plain table. – TMG May 06 '15 at 17:26
  • 1
    @TMG Yeah I agree you would have to handle it on a case per case basis. haha like I say in my answer I don't love it but we had a no js constraint for this particular bit so it was the best solution I could come up with :P – Loktar May 06 '15 at 17:27
  • Fair enough. The great advantage of your solution is that it doesn't need js. I just wanted to share my observation, to make it clear for the future readers about the limitations. – TMG May 07 '15 at 21:40
2

Seems very similar to this question. From there it seems that this should do the trick:

table {
  display: block; /* important */
  height: 600px;
  overflow-y: scroll;
}
Community
  • 1
  • 1
Wesley
  • 2,204
  • 15
  • 14
  • 1
    Thank you. This seems to work partially. The only problem that remains is that now the `thead` scrolls together with the table, while it should stay fixed. I am pretty sure I have seen examples where the body of the table scrolls, but I cannot reproduce it. :-( – Andrea Jan 23 '12 at 11:05
  • Hate to downvote, but as @Andrea mentioned, this answer is incorrect. – Dan Dascalescu Nov 08 '12 at 04:34
1

In Tables, For minimum table cells height or rows height use css height: in place of min-height:

AND

For Limiting max-height of all cells or rows in table with Javascript:

This script is good for horizontal overflow tables.

This script increase the table width 300px each time (maximum 4000px) until rows shrinks to max-height(160px) , and you can also edit numbers as your need.

var i = 0, row, table = document.getElementsByTagName('table')[0], j = table.offsetWidth;
while (row = table.rows[i++]) {
    while (row.offsetHeight > 160 && j < 4000) {
        j += 300;
        table.style.width = j + 'px';
    }
}

Source: HTML Table Solution Max Height Limit For Rows Or Cells By Increasing Table Width, Javascript

Community
  • 1
  • 1
Mr. Rick
  • 155
  • 1
  • 14
1

Use divs with max height and min height around the content that needs to scroll.

<tr>
    <td>
        <div>content</div>
    </td>
</tr>

td div{
    max-height:20px;
}

https://jsfiddle.net/ethanabrace/4w0ksczr/

Beefjeff
  • 371
  • 4
  • 12
0

Just try this.

<div style="max-height: 400px; overflow: scroll">

    <!--This is your table-->
    <table style="border: 1px solid red">
        <thead>
            <tr>
                <td>Header stays put, no scrolling</td>
            </tr>
        </thead>
        <tbody style="display: block; border: 1px solid green; height: 30px; overflow-y: scroll">
            <tr>
                <td>cell 1/1</td>
                <td>cell 1/2</td>
            </tr>
            <tr>
                <td>cell 2/1</td>
                <td>cell 2/2</td>
            </tr>
            <tr>
                <td>cell 3/1</td>
                <td>cell 3/2</td>
            </tr>
        </tbody>
    </table>


</div>
0

116 Version later.

Html

<div style="background:green;height:40px;width:100%;display:block;">Test Div</div>
<div class="grid-table">
  <div class="grid-row table-head">
    <div>Header 1</div>
    <div>Header 2</div>
    <div>Header 3</div>
  </div>
  <div class="grid-row">
    <div>Row 1, Cell 1</div>
    <div>Row 1, Cell 2dddddddddddddddddddddddhjbuozgbozugbouzbuozbozubozubuozbuozboudddddddddddddddddddddddddd</div>
    <div>Row 1, Cell jjjjjikniuhjiuhjuihuihuihuihiu3</div>
  </div>
<!-- Add more here -->
</div>

CSS

.grid-table {
    display: grid;
    grid-template-columns: repeat(3, minmax(min-content, 1fr));
    grid-auto-rows: auto;
    width: 100%;
    border-collapse: collapse;
        height:300px;
        overflow:auto;
  }

  .grid-row {
    display: contents;
  }

  .grid-row > div {
    padding: 10px;
    border: 1px solid #ccc;
        word-break: break-all;
  }
    
  .grid-row:first-child > div {
    background-color:darkgreen;
        color:white;
  }
    
    .table-head > div {
        position:sticky;
        top:0;
    }

Feel free to add many entrys as you want.

JSFiddle

Trackhe
  • 21
  • 1
  • 7
-1
<table  style="border: 1px solid red">
            <thead>
                <tr>
                    <td>Header stays put, no scrolling</td>
                </tr>
            </thead>
            <tbody id="tbodyMain" style="display: block; border: 1px solid green; height: 30px; overflow-y: scroll">
                <tr>
                    <td>cell 1/1</td>
                    <td>cell 1/2</td>
                </tr>
                <tr>
                    <td>cell 2/1</td>
                    <td>cell 2/2</td>
                </tr>
                <tr>
                    <td>cell 3/1</td>
                    <td>cell 3/2</td>
                </tr>
            </tbody>
        </table>


Javascript Section

<script>
$(document).ready(function(){
   var maxHeight = Math.max.apply(null, $("body").map(function () { return $(this).height(); }).get());
   // alert(maxHeight);
    var borderheight =3 ; 
    // Added some pixed into maxheight 
    // If you set border then need to add this "borderheight" to maxheight varialbe
   $("#tbodyMain").css("min-height", parseInt(maxHeight + borderheight) + "px");             
});

</script>


please, refer How to set maximum possible height to your Table Body
Fiddle Here

Abhishek B.
  • 5,112
  • 14
  • 51
  • 90