How can I let a table's body scroll but keep its head fixed in place?

Viewed 238460

I am writing a page where I need an HTML table to maintain a set size. I need the headers at the top of the table to stay there at all times but I also need the body of the table to scroll no matter how many rows are added to the table. Think a mini version of excel. This seems like a simple task but almost every solution I have found on the web has some drawback. How can I solve this?

18 Answers

I had to find the same answer. The best example I found is http://www.cssplay.co.uk/menu/tablescroll.html - I found example #2 worked well for me. You will have to set the height of the inner table with Java Script, the rest is CSS.

Have you tried using thead and tbody, and setting a fixed height on tbody with overflow:scroll?

What are your target browsers?

EDIT: It worked well (almost) in firefox - the addition of the vertical scrollbar caused the need for a horizontal scrollbar as well - yuck. IE just set the height of each td to what I had specifed the height of tbody to be. Here's the best I could come up with:

<html>
    <head>
    <title>Blah</title>
    <style type="text/css">
    table { width:300px; }
    tbody { height:10em;  overflow:scroll;}
    td { height:auto; }
    </style>
    </head>
    <body>
    <table>
        <thead>
            <tr>
              <th>One</th><th>Two</th>
              </td>
            </tr>
        </thead>
        <tbody>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
            <tr><td>Data</td><td>Data</td></tr>
        </tbody>
    </table>
    </body>
</html>

This caused me huge headaches trying to implement such a grid for an application of ours. I tried all the various techniques out there but they each had problems. The closest I came was using a jQuery plugin such as Flexigrid (look on http://www.ajaxrain.com for alternatives), but this doesn't seem to support 100% wide tables which is what I needed.

What I ended up doing was rolling my own; Firefox supports scrolling tbody elements so I browser sniffed and used appropriate CSS (setting height, overflow etc... ask if you want more details) to make that scroll, and then for other browsers I used two separate tables set to use table-layout: fixed which uses a sizing algorithm that is guarenteed not to overflow the stated size (normal tables will expand when content is too wide to fit). By giving both tables identical widths I was able to get their columns to line up. I wrapped the second one in a div set to scroll and with a bit of jiggery pokery with margins etc managed to get the look and feel I wanted.

Sorry if this answer sounds a bit vague in places; I'm writing quickly as I don't have much time. Leave a comment if you want me to expand any further!

Here's a code that really works for IE and FF (at least):

<html>
<head>
    <title>Test</title>
    <style type="text/css">
        table{
            width: 400px;
        }
        tbody {
            height: 100px;
            overflow: scroll;
        }
        div {
            height: 100px;
            width: 400px;
            position: relative;
        }
        tr.alt td {
            background-color: #EEEEEE;
        }
    </style>
    <!--[if IE]>
        <style type="text/css">
            div {
                overflow-y: scroll;
                overflow-x: hidden;
            }
            thead tr {
                position: absolute;
                top: expression(this.offsetParent.scrollTop);
            }
            tbody {
                height: auto;
            }
        </style>
    <![endif]--> 
</head>
<body>
    <div >
        <table border="0" cellspacing="0" cellpadding="0">
            <thead>
                <tr>
                    <th style="background: lightgreen;">user</th>
                    <th style="background: lightgreen;">email</th>
                    <th style="background: lightgreen;">id</th>
                    <th style="background: lightgreen;">Y/N</th>
                </tr>
            </thead>
            <tbody align="center">
                <!--[if IE]>
                    <tr>
                        <td colspan="4">on IE it's overridden by the header</td>
                    </tr>
                <![endif]--> 
                <tr>
                    <td>user 1</td>
                    <td>user@user.com</td>
                    <td>1</td>
                    <td>Y</td>
                </tr>
                <tr class="alt">
                    <td>user 2</td>
                    <td>user@user.com</td>
                    <td>2</td>
                    <td>N</td>
                </tr>
                <tr>
                    <td>user 3</td>
                    <td>user@user.com</td>
                    <td>3</td>
                    <td>Y</td>
                </tr>
                <tr class="alt">
                    <td>user 4</td>
                    <td>user@user.com</td>
                    <td>4</td>
                    <td>N</td>
                </tr>
                <tr>
                    <td>user 5</td>
                    <td>user@user.com</td>
                    <td>5</td>
                    <td>Y</td>
                </tr>
                <tr class="alt">
                    <td>user 6</td>
                    <td>user@user.com</td>
                    <td>6</td>
                    <td>N</td>
                </tr>
                <tr>
                    <td>user 7</td>
                    <td>user@user.com</td>
                    <td>7</td>
                    <td>Y</td>
                </tr>
                <tr class="alt">
                    <td>user 8</td>
                    <td>user@user.com</td>
                    <td>8</td>
                    <td>N</td>
                </tr>
            </tbody>
        </table>
    </div>
</body></html>

I've changed the original code to make it clearer and also to put it working fine in IE and also FF..

Original code HERE

If you have low enough standards ;) you could place a table that contains only a header directly above a table that has only a body. It won't scroll horizontally, but if you don't need that...

Related