How to: Fixed Table Header with ONE table (no jQuery)
Asked Answered
T

2

7

I know, there are at least 3 dozen questions like this on stackoverflow and still, I could not make this happen:

A simple table where thead is sticked/fixed at the top, and the tbody is scrolled. I tried so much in the past days and now I ended up here crying for help.

A solution should work in IE8+ and newest FF, Chrome & Safari. The difference to other "possible duplicates like this one is that I don't want to use two nested tables or jQuery (plain javascript is fine though).

Demo of what I want:
http://www.imaputz.com/cssStuff/bigFourVersion.html.

Problem is it doesn't work in IE, and I would be fine to use some JS.

Titivate answered 16/7, 2012 at 7:40 Comment(4)
Not that its relevant to the technical discussion here, but AFAICT this imaputz.com/cssStuff/bigFourVersion.html works on IE10 OK (perhaps earlier versions cratered).Legislation
Are you sure that this doesn't work in IE for you? Which IE? The linked page says it should work in IE6+.Politicize
@MattKantor it doesn't work in IE8 (tested with IE10 in IE8-mode).Titivate
@Garry it's not - as I mentioned, I don't want to use two nested tablesTitivate
T
14

Ok i got it:

You need to wrap the table in two DIVs:

<div class="outerDIV">
  <div class="innerDIV">
    <table></table>
  </div>
</div>

The CSS for the DIVs is this:

.outerDIV {
  position: relative;
  padding-top: 20px;   //height of your thead
}
.innerDIV {
  overflow-y: auto;
  height: 200px;       //the actual scrolling container
}

The reason is, that you basically make the inner DIV scrollable, and pull the THEAD out of it by sticking it to the outer DIV.

Now stick the thead to the outerDIV by giving it

table thead {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
}

The tbody needs to have display: block as well.

Now you'll notice that the scrolling works, but the widths are completely messep up. That's were Javascript comes in. You can choose on your own how you want to assign it. I for myself gave the TH's in the table fixed widths and built a simple script which takes the width and assigns them to the first TD-row in the tbody.

Something like this should work:

function scrollingTableSetThWidth(tableId)
{
    var table = document.getElementById(tableId);

    ths = table.getElementsByTagName('th');
    tds = table.getElementsByTagName('td');

    if(ths.length > 0) {
        for(i=0; i < ths.length; i++) {
            tds[i].style.width = getCurrentComputedStyle(ths[i], 'width');
        }
    }
}

function getCurrentComputedStyle(element, attribute)
{
    var attributeValue;
    if (window.getComputedStyle) 
    { // class A browsers
        var styledeclaration = document.defaultView.getComputedStyle(element, null);
        attributeValue = styledeclaration.getPropertyValue(attribute);
    } else if (element.currentStyle) { // IE
        attributeValue = element.currentStyle[vclToCamelCases(attribute)];
    }
    return attributeValue;
}

With jQuery of course this would be a lot easier but for now i was not allowed to use a third party library for this project.

Titivate answered 4/8, 2012 at 9:56 Comment(5)
It should be - take the first TD-row in the tbody width and assign it to th width, the other way around. ths[i].style.width = getCurrentComputedStyle(tds[i], 'width');Tsaritsyn
Sure you can do it the other way around. In this specific usecase i had to do it this way, because the user defines the widths of the TH before (dynamicly created table) and it defines the width of the whole tableTitivate
ok, sounds like its diff use cases, hence handled differently.Tsaritsyn
Sorry, but I didn't understand the 2nd function? what do I do with it?Aramaic
just insert the second function somewhere. It's called by the first function. It'll calculate the computed CSS styles for a given element (this, of course, is a lot easier with jQuery and the likes).Titivate
C
-1

Maybe we should change a method to archieve this goal.Such as:

<div><ul><li>1</li><li>2</li></ul></div> //make it fixed
<table>
    <thead>
        <tr><th>1</th><th>2</th></tr>
    </thead>
    <tfoot></tfoot>
    <tbody></tbody>
</table>

Of course, this is not good to sematic.But it is the simplest way without js or jq. Don't you think so?

Carbonous answered 27/3, 2014 at 4:44 Comment(7)
no, I don't. Your div doesn't need to be fixed. It's not part of the scrolling table body. The widths would also be totally wrong compared to the table cell widths. Your answer is the same as Gandalfs below and does not work. This is not possible without JS.Titivate
Are you sure? Give me your email, and I will send my example to you.Carbonous
If your talbe is at the top of viewport, my solution will be fine. If not, make the display of that div none at start and make it block when the talbe scolls to the top and make it none again when the talbe can't be seen. Of course, some js should be used.Carbonous
Yeah,right. Now you can go jsfiddle.net/JChen___/3fUFV to see my example.Carbonous
great. First of all, your solution breaks apart when used in a real website, with other content, text, elements and of course, lots of these tables. Second: your solution is not flexible. 5 tables with different number of rows and content and it breaks apart. You would have to specifiy widths individually for each table which isn't an acceptable solution. Now take 100 server generated tables. You can't use your solution for dynamic and flexible layouts/contents/frameworks.Titivate
Good analysis.But I believe that they can all be solved with some simple js, although I did not try.Carbonous
You're right. They can be solved with simple JS. At least one of it. See the accepted answer for the script.Titivate

© 2022 - 2024 — McMap. All rights reserved.