Link to home
Start Free TrialLog in
Avatar of mjcoyne
mjcoyne

asked on

Creating a static header in *huge* scrolling table via CSS?

I have ported a spreadsheet to a web page, and need the top portion of the data to remain fixed.

I'm somewhat close to a solution (thanks to scouring around in these forums and Googling madly), but it's not *quite* there yet...

The page is essentially three separate tables; I need the first two to remain static and the third one to scroll.

A truncated version of the table is available at http://www.comstocklab.com/9343-short.html 

What I've done to this point is assign a noScroll class to the rows of the two first tables; in the stylesheet, that class looks like this (essentially I stole this from another thread here, credit goes to the original author):

.noScroll {
     position: relative;
     top: expression(this.offsetParent.scrollTop);
     height: 10px;
     background: White;
}

(CSS essentially from original thread at https://www.experts-exchange.com/questions/21034218/scroll-table-column-headers-stay-put.html)

This *almost* works -- see http://www.comstocklab.com/9343-short.html -- but not quite.

The main data table (which in the full-sized version is some 5,000 lines long -- I truncated the above table to 200) seems to be "sliding under" the header tables (as it should), but there are several problems:

First, there is a line of data that shows up *above* the static portion when you're scrolling through.  Second, it seems very "jumpy", probably due to the JS having to re-write the static portion over and over? (I know Perl pretty well, but not JS :)).  And thirdly, as one final annoyance, IE complains about Active Content when Windows security is on...

Is there a better way to do this?  Can one just put the header tables in a static DIV with a different z-index or something?

BTW, cross-browser compatibility is *not* an issue with this particular project -- if it works in IE, we're good to go...

I was hoping to have this done in time for work on Monday (it's Sunday afternoon here on the US East Coast) -- any help greatly appreciated...
Avatar of softplus
softplus

Hi mjcoyne,
Excuse me for asking a stupid question, but how about a frameset? You can just put the header into the top frame and the data into the lower frame; each frame can be scrolled seperately. That's how I would do it, not that I'm a reference or anything :)
John
Avatar of mjcoyne

ASKER

Thanks for the suggestion, softplus!

The problem with using frames (unless I'm missing something) is that horizontal scrolling in each frame is independent.  If you look at the table, you'll see that it's quite big horizontally -- 23 columns of data.  Thus, the frames idea would work as long as scrolling was only done vertically -- as soon as you scrolled one window horizontally to see the columns of data that won't fit on the screen initially, the column headers (in the upper frame) would no longer coincide with the data columns (in the lower frame).

Again, I haven't used frames in quite some time, so I might be missing something...

This is a tricky problem, and it really shouldn't be -- you'd think that the powers-that-be would have considered "keep the headers static and scroll the data" a common thing...
Avatar of mjcoyne

ASKER

It seems like changing the CSS line above to:

     top: expression(this.offsetParent.scrollTop -15);

fixes the "line at the top" issue, so we're even closer.  It also seems easier to assign this class to a <DIV> that holds both of the header tables, this seems to work okay.  I expected assigning this class to a <thead> tag would work, but it doesn't.  The scroll with this method is still unacceptably jumpy -- unless it's just my LCD?

All the examples I'm finding on the web rely on the table being only part of the page, certainly not a couple of screens horizontally.  Most of these solutions rely on a <DIV> of set height and width, so there are scroll bars to scroll the data under the header.  The problem with these solutions in this case is that it puts the scroll vertical scroll bar on the left, thus you have to go a few screens over to scroll down.

I tried using "direction: rtl" to put the scrollbar on the left, but it (understandably) reversed all text in the table...
Well, I don't see any data peaking through with your sample page.

You might be able to get a smoother scrolling effect by applying this CSS hack, but other than that you won't get anything better than what you've got. Javascript won't do any better. The hack also won't cause any security warnings (that's 'cause you're using the proprietory IE code expression(), which is now considered suspect with SP2.)

http://www.stunicholls.myby.co.uk/layouts/fixed.html



You something with a little smother scrolling, and cross-browser as well

Cd&

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html>
<head>
<style>
td, th {padding:0px; border:1px solid black;margin:0}
tr {height:25px; border:1px solid black;}
tbody {width:600px;height:300px;overflow:auto;}
thead {display: table-header-group;}
.scroller {width:615px;height:300px;overflow-y:auto;
            overflow-x:hidden; }

</style>
<script type="text/javascript">
function setWidth()
{
   if(document.all)
   {
      tabwidth=Math.max(header.offsetWidth,content.offsetWidth);
     
      header.style.width=tabwidth;
      content.style.width=tabwidth;
      val=Math.max(td1.offsetWidth,th1.offsetWidth);
      th1.style.width=val;
      td1.style.width=val;
      val=Math.max(td2.offsetWidth,th2.offsetWidth);
      th2.style.width=val;
      td2.style.width=val;
      val=Math.max(td3.offsetWidth,th3.offsetWidth);
      th3.style.width=val;
      td3.style.width=val;
      val=Math.max(td4.offsetWidth,th4.offsetWidth);
      th4.style.width=val;
      td4.style.width=val;
   }
}
onload=setWidth;
</script>    
</head>
<body>
<table style="width:600px;" id="header">
<thead>
<tr>
<th id="th1">Test</th>
<th id="th2">Hello</th>
<th id="th3">Yoohoo</th>
<th id="th4">Hmm</th>
 <![if lt IE 6 ]>
<th style="width:15px;border:none">&nbsp;</th>
<![endif]>
</tr>
</thead>
<!--[if gte IE 5]>
</table>
<div class="scroller">
<table style="width:600px;" id="content">
<![endif]-->
<tbody>
<tr>
<td id="td1">Data</td>
<td id="td2">Hello</td>
<td id="td3">I am here and so are youuuuuuuuuuuuuuuuuuuuu</td>
<td id="td4">Yoohoo</td>
</tr>
<tr>
<td>Dataaaaaaaa</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are youuuuuuuuuuuuuuuuuuuuuuu</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
<tr>
<td>Data</td>
<td>Hello</td>
<td>I am here and so are you</td>
<td>Yoohoo</td>
</tr>
</tbody>
</table>
<!--[if gte IE 5]>
</div>
<![endif]--></body>
</html>
I agree with Cd&, you should have one table only...

You might want to consider not using any "static" header...  instead you can repeat the header every 10 ore 20 lines or so...
It's not exactly the same thing, but at least you can be sure it will display correctly.
Here is an example (repeating header every 3 rows):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Table</title>
</head>
<body>

<table border="1">
<tr><th>Test</th><th>Hello</th><th>Yoohoo</th><th>Hmm</th></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><th>Test</th><th>Hello</th><th>Yoohoo</th><th>Hmm</th></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><th>Test</th><th>Hello</th><th>Yoohoo</th><th>Hmm</th></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
<tr><td>Dataaaaaaaa</td><td>Hello</td><td>I am here and so are you</td><td>Yoohoo</td></tr>
</table>

</body>
</html>
Avatar of mjcoyne

ASKER

Thank you COBOLdinosaur and GrandSchtroumpf for your replies...

COBOLdinosaur -- while your method works well and is elegant, it relies on a scrollbar to the right, and thus is of most use for tables that don't occupy more than one screen horizontally.  On my table, people will likely scroll vertically to a particular gene (say BF2765; from the data in the first column) and then want the particulars about that gene (from the data occupying that row).  If they're not looking for a gene by name, the next most likely things would be to look for a gene by its position on the chromosome (the second and third columns) or by its description (the tenth column).

Forcing people to use a scroll bar on the right side of the table pushes that primary data off the screen.  Thus, while solving the problem of needing static headers, it introduces another equally troublesome problem (in the spreadsheet in which I collected this data to begin with, both of these issues were solved easily by locking the cells so the header rows remained on a vertical scroll, and column one remained locked on-screen on a horizontal scroll).

GrandSchtroumpf -- your idea would obviously work (and if there is no other solution, I may need to do something like this).  My immediate concern is file size.  As I mentioned above, this is a huge file -- it's already over 106,000 lines long and checks in a 6.25 MB (the Excel file was 14 MB).  I've already stripped the file as much as I can to reduce its size (all 4.01 optional tags omitted, no CR/LF throughout the file, etc).  Here's the consequence I'm envisioning:

The table has some 4,400 rows of data.  My screen (a 19" at 1280 x 1024) holds ~18 rows of data.  The HTML to produce the minimal required header (the last five rows of the static portion, beginning with "Significant BLAST matches..." over on the right and ending with a single blank row before the data starts) comes in at about 3 KB.  Let's say we could drop that by a third, to 2 KB, and that we'd repeat it every 15 rows.  If this were the case, we'd be repeating the header code almost 300 times, and adding almost 600 KB (about 10%) to the file size.  That calculation may overstate the impact somewhat, but you see what I mean...

The optimal solution here would include a smoothly scrolling header that uses the browser's normal scroll controls, and perhaps a way to lock column one in place on a vertical scroll (admittedly, a new element to the question, and one of secondary importance compared to the need for the static headers).

I may need to forgo having all the data available at once and go the CGI route, where a subset of genes would be retrieved by a query...

Who knew this would be so difficult to do in HTML/CSS?
Avatar of mjcoyne

ASKER

Sorry, StormyWaters --  I meant to acknowledge your post as well.  I'll have to check out the method used on that page.  Again though I wonder how it will behave when the static data is not a small rectangular menu, but  a couple of horizontial screenfulls of data?
>>>Who knew this would be so difficult to do in HTML/CSS?

What you are doing is realy not an appropriate application for a web page.  You should be using a spreadsheet.  its like trying to transpost 100 tonnes of rock with a wheelbarrow.  It can be done, but not very efficiently or reliably; and a sinle railcar would handle it quite nicely. you might want to consider making the data avail in th eform of a spreadsheet.

Cd&
6.25 MB / 5000 lines is not appropriate for a web page anyway.
you should put all the data in a database.
then you can split the table into several different pages and provide some simple page navigation... something like this:  <<prev | 1 | 2 | 3 | 4 | 5 | 6 | next>>
and also provide some search functionality.
Avatar of mjcoyne

ASKER

Yes, I agree.  I already have the data in a spreadsheet -- the reason I'm attempting to get it on the web is then it's accessible from anywhere -- no need to have the spreadsheet to access the data, just a web browser.

I've started working on a Perl/CGI solution that will allow more interactive functionality (showing only those entries that contain a particular term in the description, for example).  This is ultimately where I was going anyhow, I just thought in the interim I could dump the thing to some reasonably usable web page until I can get the Perl code written.

Still, the problem I'm experiencing here is not directly related to the size of the HTML file -- say the file only had 200 entries in it (as the shortened version linked to above does), the same problem would still exist...

Thanks for all your help thus far -- I'm still open for other ideas regarding this issue if anyone has any...
Avatar of seanpowell
There is another way - back in a moment...
ASKER CERTIFIED SOLUTION
Avatar of seanpowell
seanpowell
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If your page has 200 entries, then you can repeat the headers every 20 entries...


The scrolling behaviour that you want cannot be acheived in html.
You can do it in spreadsheet programs, or using Java Swing (JTable), but not yet using html.
Maybe a future CSS spec will provide some property for fixed table rows/cols.

Viewports are all you can use in HTML.
The viewport is kind of like a "window" that is used to view your content.
When the content is bigger than the Viewport, you can use the scrollbars to change the portion of your content that is visible through the Viewport.

Now, when you have a fixed table header:
1) on vertical scrolling, the header must not move, so it must not be part of the viewport.
2) on horizontal scrolling, the header must follow the scrolling, so it must be inside the viewport.
Conclusion:  it must be at the same time inside and outside the viewport, which is impossible.

This ends the demonstration that fixed table headers are impossible using viewports only.
Avatar of mjcoyne

ASKER

seanpowell --

WOW!  That's an impressive solution!  It handles the header information perfectly, scrolls very cleanly, overcomes the scrollbar on the right issue, and by dumping all the CSS class entries, it will likely reduce my file size by about 2 MB.

I'm very impressed!

I think this'll be the answer -- I just need to try it out with the full data set, and I'll likely be awarding you the points...  I'll whip up a Perl one-liner to strip all the class="whatever" entries and give it a go...

I do notice I lose right-aligned numbers, but that's not such a big deal -- I can put them back (or not take them out to begin with) if need be...

While I've got you, is there a way you know of to lock data column 1 (orf names) so it remains in place during a horizontal scroll?  I guess if not, I could repeat the column on the far right of the table, but your use of the COL element makes me (perhaps unjustly) optimistic that there might be a way to do this?

Great work, seanpowell.

Mike
Thanks Mike.

You could scroll the data both ways:
http://www.pdgmedia.com/ee/both-ways.html
but it means pulling out your initial column data into a separate table - that idea bothers me.
(This also has some basic js that highlights each cell on mouseover which can be helpful in large tables, but that's another story)

I much prefer your second suggestion, simply repeating the data at the end, like this:
http://www.pdgmedia.com/ee/gnomes_2.html

Again, this is all IE, for the record so to speak.

Sean
Nice examples Sean, that looks pretty in IE.
Avatar of mjcoyne

ASKER

Sean --

This solution works wonderfully!  Well worth the 500 points.

You might consider posting your method here in another form, so it's not lost if/when you take the example pages down from your server...

I do agree with you -- perhaps duplicating that first column of data is the better way to go.  I'll play with both methods, and see how it goes.

Again, thank you for all for your efforts, and especially to Sean for a super solution!

Mike
Always glad to help MIke, and thanks for the A :-)

Sean