5

I've read numerous articles on this topic and tried implementing a few, but I just can't seem to get it right. I have a simple HTML table, which compares products:

<table>
  <tr>
    <th>Product:</th>
    <th>Product 1</th>
    <th>Product 2</th>
    <th>Product 3</th>
  </tr>
  <tr>
    <td>Features</td>
    <td>Product 1 features</td>
    <td>Product 2 features</td>
    <td>Product 3 features</td>
   </tr>
   and so the list goes on...
 </table>

What I want is for the header row to always be on top of the active window, that is the top row when I scroll down, since my rows go down quite far. I want to keep the product names in the tags always visible so that a user can relate the content to the different products in the very top row all the time.

Thank you in advance!

jcmeloni
  • 1,259
  • 1
  • 15
  • 21
DextrousDave
  • 6,603
  • 35
  • 91
  • 134

4 Answers4

6

I simple technique is to have the header row in a separate table from the actual data. This header table is set to have it's position as fixed, and then the table below, which holds the results, has it's margin-top set the height of the header table.

This creates the effect of the header staying where it is and the table scrolling.

A basic example here: http://jsfiddle.net/zKDR4/2/

You can also create a fixed header with jQuery plugins. Take a look at this one http://fixedheadertable.com/ really simple to implement.

Edit

As Anders has mentioned, if you go down my suggested route, you will need to set the widths of the th and td elements. Otherwise they would not match up since they are separate tables.

Tim B James
  • 20,084
  • 4
  • 73
  • 103
  • 2
    This will not work well if the widths of th and td differ, which they usually do. If the cells (including headers) have a fixed and equal size, this works very well however. (+1 still, best solution I think, and also nice jquery tip) – Anders Arpi Jan 23 '12 at 14:53
  • @AndersHolmström yeah you need to set the widths of the `th` and `td` in this case. Best solution I feel though is using a jquery plugin which builds the header table from the table so that the widths are correct. – Tim B James Jan 23 '12 at 15:09
  • 1
    Good answer thanks, it works well. However, when viewed on a small screen (mobile screen) , you have to scroll horizontally. Now that poses a problem, since when you scroll horizontally, the fixed header scrolls horizontal as well(since it is fixed, and it partly out of the window). How do I only fix it vertically? – DextrousDave Jan 27 '12 at 11:21
  • For a mobile screen I would try to get rid of the horizontal scrolling. Look into Responsive CSS. – Tim B James Jan 27 '12 at 12:14
0

I had the problem for a planning with row headers (hours) and column headers (days). I wanted to keep visible the both. The application shows the content in an IFrame, so I've created an horizontal DIV and a vertical DIV outside the IFrame with a style "overflow:hidden". Then I've synchronized the scrollings with the "onscroll" event of the IFrame.

Here is my code to synchronize headers with scrolling:

window.onscroll = function () {

        try {
            // - Scroll days header (on the top) horizontally
            var offsetX = (document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft; // Source: http://stackoverflow.com/questions/2717252/document-body-scrolltop-is-always-0-in-ie-even-when-scrolling
            var header1Div = window.parent.document.getElementById("EntetesColonnes");
            header1Div.scrollLeft = offsetX;

            // - Scroll hours header (on the left) vertically 
            var offsetY = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; // Source: http://stackoverflow.com/questions/2717252/document-body-scrolltop-is-always-0-in-ie-even-when-scrolling
            var header2Div = window.parent.document.getElementById("HeaderHoursLeft");
            header2Div.scrollTop = offsetY;
        }
        catch (ex) {
            alert("FrmPlanningChirurgien/window.onscroll: " + ex.message);
        }
    }

My solution is not that simple, because I had to set in "onresize" the width of horizontal DIV and the height of vertical DIV. Then this induces more complexity, because onResize could be fired a lot of times per second, and this event is even fired in IE8 when scrolling occurs. So I've done a setTimeout to prevent headers to be redrawn too often :

var PREVIOUS_frameWidth = 0;
var PREVIOUS_frameHeight = 0;
var timerMakeHeaders = null;

window.onresize = function () {
    try {

        var frameWidth = CROSS.getWindowWidth();
        var frameHeight = CROSS.getWindowHeight();

        if (frameWidth != PREVIOUS_frameWidth || frameHeight != PREVIOUS_frameHeight) {

            // - *** Launch headers creation method
            // - If there is another query to redraw, the Timer is stopped and recreated.
            // - The headers are only redrawn when Timer fires.
            if (timerMakeHeaders != null) {
                window.clearTimeout(timerMakeHeaders);
                timerMakeHeaders = null;
            }
            timerMakeHeaders = window.setTimeout(makeHeaders, 50); 

            // - *** Store new values
            PREVIOUS_frameWidth = frameWidth;
            PREVIOUS_frameHeight = frameHeight;
        }
    } catch (e) { alert("Erreur window.onresize/FrmPlanningChirurgien.aspx : " + e.message); }
}

And finally the makeHeaders() method have to resize DIVs :

function makeHeaders() {

    // (...)

    var frame = window.parent.document.getElementById("CalendarFrame"); 
    var iframeRect = frame.getBoundingClientRect(); 
    headerDiv.style.width = iframeRect.right - iframeRect.left - 20; // The 20 pixels are here for the vertical scrollbar width
    headerDiv.scrollLeft = frame.scrollLeft;

    // (...)


    var headerHourDiv = window.parent.document.getElementById("HeaderHoursLeft");
    var newHeight = iframeRect.bottom - iframeRect.top - 20 - 7; // The VISIBLE width for the DIV must be equal to IFRAME Width minus the scroll width or height
    headerHourDiv.style.height = newHeight; 
    headerHourDiv.scrollTop = frame.scrollTop;       


}
catch (e) {
    alert("makeHeaders: " + e.message);
}    }

Perhaps it could be possible not to use an IFRAME, and to use only 3 DIVS: one for each header, and the last one for the content. But the mechanism have to be the same I think : needs a synchronization between scrolling positions.

Best regards,

Elo
  • 2,234
  • 22
  • 28
-1

I have created an example using JSFiddle, which you can view as a demo.

The only downside is that you will need to specify column sizes as when you use position: fixed you lose the widths on the header.

This is the CSS for the example, the HTML is the same as that you have supplied.

thead {
    height: 1em;
}

thead tr {
    position: fixed;
    background-color: #FFF;
}
Fenton
  • 241,084
  • 71
  • 387
  • 401
-2

You can do this by make a div and fix the position . or it can also done by adding inline css

<table style="position:fixed;">
  <tr>
     <th>Product:</th>
     <th>Product 1</th>
     <th>Product 2</th>
      <th>Product 3</th>
  </tr>
  <tr>
    <td>Features</td>
     <td>Product 1 features</td>
     <td>Product 2 features</td>
     <td>Product 3 features</td>
   </tr>
  </table>
  • 2
    This does not do what the question asks. It just fixes the whole table to one place on the page. – srk Dec 04 '14 at 03:44