Larry Steinle

November 30, 2011

CSS Table Scrollbar with Fixed Header and Footer

Filed under: Web — Larry Steinle @ 11:12 pm
Tags: ,

I am truly surprised that CSS3 failed to include scrollbar support for the TBody tag. However, with a bit of finagling we can create a great looking scrollable table with the help of a single div tag!

An HTML table is divided into three sections: header (THead), body (TBody) and footer (TFoot). Often there is a need to have a set height for the table with the body of the table being scrollable. Unfortunately the TBody tag doesn’t support the CSS overflow style attribute. Today’s post we will add a scrollbar to the TBody tag using a little CSS creativity.

Browser Table Behaviors

Contrary to popular opinion tables are not implemented consistently across browsers. There are behavior differences between browsers because of the CSS differences. Before we begin creating a scrollable table we will first address these differences so that our tables will behavior more consistently across browsers.

First, the height of the rows will differ between browsers. This is caused by the line-height. I like targeting the look and feel of sites for the IE platform as it continues to be the most widely used platform. IE uses a line-height of 18 pixels.

I prefer to set cellpadding and cellspacing to zero by using border-collapse:collapse with margin and padding set to zero pixels. Setting the padding value has the added benefit of addressing the IE box model behavior for tables. The IE box model is unique among browsers in that it does not adhere strictly to CSS guidelines by excluding the border size from the width/height of the tag.

Next I’ll disable the border on the table as I prefer to control the border from the table header (th) and table definition (td) tags. Finally I’ll add a background-color to the th tags.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}
</style>

Strategy

Since the TBody doesn’t support the overflow tag we need to use a tag that does. The table needs to be wrapped inside a div tag with a set height and scrollbar. The following is the html that will be used for the post. The part I like about the CSS approach is the support for a standard HTML table. All that we have to add is a wrapper div. Small price to pay for scrollbar support!

<div class="vScroll">
<table>
<thead><tr><th>Heading 1</th><th>Heading 2</th></tr></thead>
<tfoot><tr><th>Footer 1</th><th>Footer 2</th></tr></tfoot>
<tbody>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
</tbody>
</table>
</div>

Now we begin by setting the height for our example. Overflow-y is used to manage the scrollbar for the table. We will avoid using Overflow-x. If you need horizontal scrolling you will need to create yet another host div tag for horizontal scrolling or the headings will fail to align when the scroll bar is used. So the sequence would be a horizontal host div tag with an inner div tag for vertical scrolling with an inner table. However, to keep things simple today we will focus on implementing a simple vertical scrollbar. Once you understand how to create the vertical scrollbar it shouldn’t be too much of a stretch to create a horizontal scrollbar when needed.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-y:auto;}
</style>

Scrollable Table Example:

I apologize that this is the only example I can give. The version of WordPress that I am using does not permit me to demonstrate the affect of the styles on the tags from this point forward. So it would be best to copy the html table code above and update the styles with each section into your own page so you can see the behavior changes.

Heading 1 Heading 2
Footer 1 Footer 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2
Data 1 Data 2

Scrollable Table with Fixed Header

While we have the table set to the desired height scrolling causes the header to move in and out of view along with the body content. Fortunately we can control the positioning of the THead tag. Since the table is wrapped inside the div tag once we position the THead tag it will fall outside the div tag. To ensure there will be space for the THead tag outside the div tag we will add a margin to the top of the div tag. Next we will position our THead tag.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-y:auto;margin-top:22px;}
.vScroll thead {position:absolute;margin-top:-21px;}
</style>

Scrollable Table with Fixed Footer

Similarly the table footer is scrolling with the content instead of being in a fixed position at the bottom of the table. Once again we will ensure there is room available to host the footer by applying margin to the bottom of the div tag and then we will position the footer below the div tag.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}
/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-y:auto;margin-top:22px;margin-bottom:22px;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}
</style>

Problem: Header and Footer Columns are Different Widths Than Data Columns

Unfortunately once the table header and footer are absolutely positioned the browser is no longer able to calculate the width of these columns accurately. The easiest way around this problem is to set a fixed with on the columns in the header, body and footer tags. Set the width in the HTML so that the CSS can be used for more than a single table.

<div id="vScroll">
<table>
<thead><tr><th width="125px">Heading 1</th><th width="125px">Heading 2</th></tr></thead>
<tfoot><tr><th width="125px">Footer 1</th><th width="125px">Footer 2</th></tr></tfoot>
<tbody>
<tr><td width="125px">Data 1</td><td width="125px">Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 1</td><td>Data 2</td></tr><tr><td>Data 1</td><td>Data 2</td></tr>
</tbody>
</table>
</div>

Content Changes Width of Columns

Unfortunately if the content is too wide to fit inside the defined limits of the tag it will stretch the tag in the body but not the header or footer. To minimize the risk of this problem use the css word-wrap attribute to allow the text to break even if it is in the middle of a word. Before the word-wrap can assist the layout problem the fixed table-layout option must be added to the table tag.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-y:auto;margin-top:22px;margin-bottom:22px;}
.vScroll table {table-layout:fixed;word-wrap:break-word;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}
</style>

Fix Missing Left Border in IE 8 and Mozilla

Oddly enough in Mozilla and IE 8 (and earlier versions of IE) the left border fails to display. The easiest way to address this problem is to disable the left border on the first cell in the body tags and use the host div tag’s left border instead. Because of the border the header and footer will need to be repositioned to the left the same number of pixels as the size of the border.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-y:auto;margin-top:22px;margin-bottom:22px;}
.vScroll table {table-layout:fixed;word-wrap:break-word;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}

/* Fix Positioning Issue in IE 8 (and Earlier) and Mozilla */
.vScroll {border-left:solid 1px silver;}
.vScroll td:first-child {border-left:none;}
.vScroll thead, .scroll tfoot {margin-left:-1px;}
</style>

Fix Chrome, Mozilla, Opera and Safari to Hide Horizontal Scrollbar

Another issue is that while Internet Explorer hides the horizontal scroll bar, Chrome, Mozilla, Opera and Safari continue to display the scrollbar. To force consistent behavior between browsers disable the horizontal scrollbar. Be careful with the width of the div tag. In some browsers the horizontal scroll bar will be re-enabled if the content falls outside the limits of the host tag. Otherwise you will need to use overflow-x:hidden.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-x:none;overflow-y:auto;margin-top:22px;margin-bottom:22px;}
.vScroll table {table-layout:fixed;word-wrap:break-word;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}

/* Fix Positioning Issue in IE 8 (and Earlier) and Mozilla */
.vScroll {border-left:solid 1px silver;}
.vScroll td:first-child {border-left:none;}
.vScroll thead, .scroll tfoot {margin-left:-1px;}
</style>

Make Table Flush with Scrollbar

Finally, it would be nice if the scrollbar was flush against the table. To achieve this affect we need to set the width of the div tag with padding for the scrollbar.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-x:none;overflow-y:auto;margin-top:22px;margin-bottom:22px;width:273px;}
.vScroll table {table-layout:fixed;word-wrap:break-word;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}

/* Fix Positioning Issue in IE 8 (and Earlier) and Mozilla */
.vScroll {border-left:solid 1px silver;}
.vScroll td:first-child {border-left:none;}
.vScroll thead, .scroll tfoot {margin-left:-1px;}
</style>

Fix Safari Scrollbar

Since I’m going thru so much effort to make my table and scrollbar behave and look the same across browsers I just as well focus on the Safari scrollbar. On a MacBook Pro the safari scrollbar is very thin at roughly 6 pixels width. On all other browsers the scrollbar is set to 16 pixels width. With webkit we can modify the scrollbar so that it will be the correct width and flush against the table just like it is in other browsers. Note that the webkit commands must be at the end of the css style section as any standard css logic following the webkit commands will be ignored.

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;}
table, th, td {margin:0px;padding:0px;}

/* Add some color flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Scrollbar CSS Logic */
.vScroll {height:295px;overflow-x:none;overflow-y:auto;margin-top:22px;margin-bottom:22px;width:273px;}
.vScroll table {table-layout:fixed;word-wrap:break-word;}
.vScroll thead {position:absolute;margin-top:-21px;}
.vScroll tfoot {position:absolute;margin-top:294px;}

/* Fix Positioning Issue in IE 8 (and Earlier) and Mozilla */
.vScroll {border-left:solid 1px silver;}
.vScroll td:first-child {border-left:none;}
.vScroll thead, .scroll tfoot {margin-left:-1px;}

/* Standardize Scrollbars */
::-webkit-scrollbar {width:16px;}
::-webkit-scrollbar-track {border-radius:10px;-webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.3);}
::-webkit-scrollbar-thumb {background-color:silver;border-radius:10px;-webkit-box-shadow: inset 0 0 3px rgba{0,0,0,0.5);}
</style>

Conclusion

Today we found an easy way to add scrollbars to an HTML table body. The only drawback is that the columns must be fixed width. We discussed a way to minimize the risk of the cell width changing by allowing words to wrap even if the wrapping needs to occur within the middle of the word. As a bonus we learned how to make the table render exactly the same regardless of the browser.

In the next post we will use jQuery to set the width of the header and footer cells automatically so that the scroll table will more accurately emulate a standard table permitting flexible cell widths while eliminating the need for wrapping in the middle of words. Additionally, the jQuery logic will allow flexible heights in the header and footer instead of the fixed single row height that today’s CSS expects. To continue visit the post titled, Create Scrollable Table with CSS and jQuery.

Advertisement

2 Comments »

  1. ty so much this helped me a lot – note an error though, div tag should read class=”vscroll” not id=
    thanks again,

    Comment by someone — December 15, 2011 @ 3:18 pm | Reply

  2. Thank you very much for this detailed example and explanation.I am a little bit familiar with css and php and i think i understand your code-example. At first it seemed to be exactly what I was looking for. While testing with Dreamweaver (Live View) everything looked just fine. Showing the example in Firefox and IE both work all right on my localhost with xampp installed. But when the files (css stylesheet and php example file) are posted on the webserver it only works in Firefox and not in IE9. Do you have a clue for me where to look for a solution? Thank you in advance. CK, The Netherlands

    Comment by Conrad — July 18, 2012 @ 12:22 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: