views:

2125

answers:

6

Hi guys,

Looking for a solution to create a table with a scrollable body, and a static/fixed header.

Searching around seems to produce MANY flaky pieces of code, either not working in IE, requireing a huge amount of Javascript and tweaking, or a silly amount of CSS hacks etc.

To be honest, if it's a case of CSS hacks or Javascript, I think I'd prefer to go the Javascript option.

The alternative I guess is to place it all in a div, and just scroll the entire table - but that's a bit naff :D

Thanks!

+2  A: 

I believe that the solution is to set an explicit height for the tbody and set the overflow to auto or scroll. Unfortunately, as you've discovered, tables and CSS are a tricky combination, and IE likes to choke on it.

Thom Smith
+2  A: 
<table style="width: 300px" cellpadding="0" cellspacing="0">
<tr>
  <td>Column 1</td>
  <td>Column 2</td>
</tr>
</table>

<div style="overflow: auto;height: 100px; width: 320px;">
  <table style="width: 300px;" cellpadding="0" cellspacing="0">
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  <tr>
    <td>Value 1</td>
    <td>Value 2</td>
  </tr>
  </table>
</div>

This creates a fixed column header with the scrollable table below it. The trick is to embed the table you want to scroll in a tag with the overflow attribute set to auto. This will force the browser to display a scrollbar when the contents of the inner table are larger than the height of the surrounding .

The width of the outer must be larger than the width if the inner table to accommodate for the scrollbar. This may be difficult to get exactly right, because some users may have set their scrollbars to be wider or smaller than the default. However, with a difference of around 20 to 30 pixels you'll usually be able to display the scrollbar just fine.

CSS-Tricks also talks about using JavaScript and CSS to help with this as well so you can use highlighting. Here is the link to that article.

Dennis
While this works, it's not great from an accessibility point of view. There are no table headers (<th>) and even if there were, they would not be explicitly associated with the relevant columns since the table is broken into two tables.
pmckenna
This is actually spot on really, if I manually define the width of each column - as pmc said, maybe not ideal from accessibility but all of all the solutions I've seen, this is by *far* my favourite, thank you so much!
Nick
If your table is tightly packed at all, this doesn't seem to come out right.
quillbreaker
+1  A: 

Yeah, this looks like a good solution - but won't it mean the table rows etc are all different widths too?

Thanks

Nick
By adding <table style="width: you will be able to change the widths of the rows manually. If data changes then you will have to add javascript to input the data into a table and have it read from a text file. It would allow for a more dynamic table.
Dennis
+3  A: 

If you can fix the column widths - it's a lot easier. If you want the browser to figure out the widths, it gets a lot harder. Basically, have the table in div that scrolls (height, overflow:auto) and have that div inside a position:relative div. In the outer div, have another div position:absolute, overflow:hidden, height: whatever the header height is, set this div's innerHTML to the innerHTML of the inner div; Here is a page that demonstrates. There are lots of gotchas, but it's doable...

<html>
<head></head>
<body onload="doit();">
<div id="outer" style="position:relative;">
  <div id="inner" style="height:100px; overflow:auto;">
    <script>
       var html = '<table><tr><th>Heading 1</th><th>Heading 2</th></tr>';
       var width = Math.floor(Math.random() * 100);
       var d = '';
       for(var i = 0; i < width; i++){d += 'a';}
       for(var i = 0; i < 100; i++){
          html += '<tr><td>' + d + '</td><td>some more data</td>';
       }
       html += '</table>';
       document.write(html);
    </script>
  </div>
  <div id="secondWrapper" style="position:absolute; background:#fff; left:0; top:0; height:25px; overflow:hidden;"></div>
</div>

<script>
function doit(){
  var inner = document.getElementById('inner');
  var secondWrapper = document.getElementById('secondWrapper');
  secondWrapper.innerHTML = inner.innerHTML; 
}
</script>
</body>
</html>

Note as you refresh and the data size changes, the header matches up perfectly. That's the real trick.

Steve Brewer
I don't understand how it works at all, so it must be black magic.More seriously, I had to fiddle with the height of my TH tags and the height of secondWrapper to get it to work. It also seems to work a bit better if the table has a maximum width of 98% or so, to keep the table and the scrollbar from bumping into each other.How did you come up with/where did you get this, anyway? It's really clever.
quillbreaker
Note to others: if you have a table with editable elements, this method copies the table and thus copies the elements. You have been warned.
quillbreaker
There are a *whole* lot of gotchas. After working with this I think it is not nearly stable enough for use in any production code. It's too late to change my up vote on this answer, though, which is too bad.
quillbreaker
+1  A: 

There was also a quiz for just this sort of thing on Sitepoint, for those looking for a non-JS solution. However I found that the table footer was necessary for stopping the table headers from collapsing their widths IF the contents of the cells weren't wide enough. I ended up hiding the tfoot in the application I used this on.
It's pure HTML/CSS and works in IE6 plus modern browsers. There are some styling limitations for the header though.

+2  A: 

I've just put together a jQuery plugin that does exactly what you want. Its very small in size and really easy to implement.

All that is required is a table that has a thead and tbody.

You can wrap that table in a DIV with a classname and the table will always resize to fit in that div. so for example if your div scales with the browser window so will the table. The header will be fixed when scrolling. The footer will be fixed (if you enable a footer). You also have the option to clone the header in the footer and have it fixed. Also if you make your browser window too small and all columns can't fit...it will also scroll horizontally (header too).

This plugin allows the browser to size the columns so they aren't fixed width columns.

you just pass the DIV's classname to the plugin like so: $('.myDiv').fixedHeaderTable({footer: true, footerId: 'myFooterId'}); and the plugin will do the rest. FooterID is a element on the page that contains the mark-up for your footer. this is used if you want to have pagination as your footer.

If you have multiple tables on the page it will also work for each table you want to have a fixed header.

check it out here: http://fixedheadertable.mmalek.com/

Keep in mind its still 'beta' so I am adding new features and bug fixes daily.

Supported browsers: IE6, IE7, IE8, FireFox, Safari, and Chrome

Here is a link to my response to another person who had the same question: http://stackoverflow.com/questions/486576/frozen-table-header-inside-scrollable-div/1533619#1533619

Mark
mark, i recently researched fixed header tables to replace one we built inhouse. yours was by far the best ive found, unfortunately i couldnt use it because it performed very poorly on IE6, in particularly scrolling is very slow. Granted this was a problem on IE6/900mhz, but we have to support that.
mkoryak