简体   繁体   中英

Fixing HTML Table Header while scrolling

I have an HTML table which is quit long, and I need to fix the header from scrolling, I have found many and many of the plugins and tricks to do it, but none worked for me.

I guessed the reason nothing works for me is due to:

  1. All of the solutions detach the header from the table
  2. They use fixed width for every column to fit to keep the header on top of the content
  3. my table is very responsive and stretches to get the full width possible ( width=100% )

Do anybody have a suggestion ?

edit: thanks to ashley I updated my css technics

My hmlt table looks exactly like: http://jsfiddle.net/enPZP/4/

I think I came up with a rather nice solution to the problem, it's a little deeper than I wanted it to be but I'm really happy with the results.

在此输入图像描述

The way it works is on document ready, clones the table header, sets the clone's size to the size of the table header and places it on top of the other header. On scroll if the top of the window is below the top of the table we set the clone's top to be at the top of the window. On resize we recalcuate the <th> sizes.

Note that I placed the <th> s into a <thead> separate from the .

jsFiddle

var tableBaseTop;
var originalHeader;
var floatingHeader;

$(document).ready(function () {
    tableBaseTop = $('table th').offset().top;
    originalHeader = $('thead');
    floatingHeader = originalHeader.clone();
    floatingHeader
        .css('position', 'absolute')
        .css('top', 0);

    setFloatingHeaderSize();

    $('table').prepend(floatingHeader);

    $(window).scroll(function () {
        var windowTop = $(window).scrollTop();

        if (windowTop > tableBaseTop)
            floatingHeader.css('top', windowTop - tableBaseTop);
        else
            floatingHeader.css('top', 0);
    });

    $(window).resize(setFloatingHeaderSize);
});

function setFloatingHeaderSize() {
    var originalThs = originalHeader.find('th');
    var floatingThs = floatingHeader.find('th');
    for (var i = 0; i < originalThs.length; i++) {
        floatingThs.eq(i)
            .css('width', originalThs.eq(i).width())
            .css('height', originalThs.eq(i).height());
    }
}

Firefox header width problem

I managed to fix the problem but I couldn't figure out how to do it using jQuery's helpers. The problem was that jQuery was giving me integer sizes when the actual width/height were decimals and I needed them as floats. Here is the section of code that I changed with a working fiddle.

jsFiddle

function setFloatingHeaderSize() {
    var originalThs = originalHeader.find('th');
    var floatingThs = floatingHeader.find('th');
    for (var i = 0; i < originalThs.length; i++) {
        var padding = 
            parseFloat(window.getComputedStyle(this.originalThs[i], null).getPropertyValue('padding-left').replace('px', ''), 10) +
            parseFloat(window.getComputedStyle(this.originalThs[i], null).getPropertyValue('padding-right').replace('px', ''), 10);

        floatingThs.eq(i)
            .css('width', this.originalThs[i].offsetWidth - padding)
            .css('height', this.originalThs[i].offsetHeight - padding);
    }
}

Update: I made this in to a reusable library shortly after coming up with the solution, view on GitHub .

I have modified your fiddle with this it fixes the Header to the top of the grid. but it does have a drawback of fixing the tile to the top left.

hopefully this will help you on your way to a full answer.

table {
    width: 100%;
    border-collapse: collapse;
    position: relative;
}
div.scoll {
    overflow: scroll;
    height: 300px;
}
.center {
    text-align: center;
}
thead {
    position: static;
    width: 100%;
}
thead tr {
    position: fixed;
}

and changed you html to look something like

<div class="scoll">
<table>
        <thead>
            <tr>
                <th>Code</th>
                <th>Rank</th>
                <th>Code</th>
                <th>Icon</th>
                <th>Message Text</th>
                <th>Progress</th>
                <th>Current</th>
                <th>X</th>
                <th>Y</th>
                <th>Z</th>
                <th>W</th>
                <th>Result</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td colspan="12">&nbsp;</td>
            </tr>
            <tr class="data-row" id="19">
                <td class="center">DDD</td>
                <!-- 4 alpha numeric -->
                <td class="center">111</td>
                <!-- 3 digits -->
                <td class="center">222</td>
                <!-- 3 digits -->
                <td class="center">
                    <img width="20" height="20" src="http://cdn1.iconfinder.com/data/icons/CrystalClear/128x128/apps/ark2.png">
                    <!-- icon -->
                </td>
                <td>Lorem ipsum</td>
                <!-- text may be long -->
                <td class="center">333</td>
                <!-- 3 alpha numeric -->
                <td class="center">444</td>
                <!-- 3 alpha numeric -->
                <td class="center">555</td>
                <!-- 3 alpha numeric -->
                <td class="center">666</td>
                <!-- 3 alpha numeric -->
                <td class="center">777</td>
                <!-- 3 alpha numeric -->
                <td class="center">888</td>
                <!-- 3 alpha numeric -->
                <td class="center">999</td>
                <!-- 3 alpha numeric -->
            </tr>
</tbody>
</table>
</div>

Seperate head row to another table and add some jquery code:

html:

<table class="headtable">
<tr>
    <td>name</td>
    <td>value</td>
    <td>action</td>
</tr>
</table>
<div class="scrolltable">
    <table class="valuetable">
        <tr>
            <td>hay 1</td>
            <td>1</td>
            <td>edit</td>
        </tr>
<tr>
            <td>hay 1</td>
            <td>1</td>
            <td>edit</td>
        </tr>
        <tr>
            <td>hay 1</td>
            <td>1</td>
            <td>edit</td>
        </tr>
        <tr>
            <td>hay 1</td>
            <td>1</td>
            <td>edit</td>
        </tr>
        </table>
</div>

js code:

$('.headtable tr').children().each(function(index)
    {      
      $(this).width($('.valuetable tr:first td').eq(index).width());
    });

style if need:

.scrolltable{height:500px; overflow:auto;}

if you want it auto resize (responsitive):

$(window).resize(function(){
$('.headtable tr').children().each(function(index)
        {      
          $(this).width($('.valuetable tr:first td').eq(index).width());
        });
});

If fixed- height column headers are acceptable, then this problem can be solved with pure CSS.

Here's an example that is configured with a fluid table width and auto column widths, just like the OP's table: jsFiddle

Note that the vertical scrollbar only appears when necessary in that example, so you may have to resize the frame to see it.

The code below is the same as the fiddle, but with a fixed table height. The scrollbar always shows in that configuration as long as there are enough rows to exceed the specified height:

HTML

<div class="scrollingtable">
  <div>
    <div>
      <table>
        <caption>Top Caption</caption>
        <thead>
          <tr>
            <th><div label="Column 1"/></th>
            <th><div label="Column 2"/></th>
            <th><div label="Column 3"/></th>
            <th>
              <!--more versatile way of doing column label; requires 2 identical copies of label-->
              <div><div>Column 4</div><div>Column 4</div></div>
            </th>
            <th class="scrollbarhead"/> <!--ALWAYS ADD THIS EXTRA CELL AT END OF HEADER ROW-->
          </tr>
        </thead>
        <tbody>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
        </tbody>
      </table>
    </div>
    Faux bottom caption
  </div>
</div>

CSS

<!--[if lte IE 9]><style>.scrollingtable > div > div > table {margin-right: 17px;}</style><![endif]-->
<style>
/*the following html and body rule sets are required only if using a % width or height*/
html {
  width: 100%;
  height: 100%;
}
body {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 20px 0 20px;
    text-align: center;
}
.scrollingtable {
  box-sizing: border-box;
  display: inline-block;
  vertical-align: middle;
  overflow: hidden;
  width: auto; /*if you want a fixed width, set it here, else set to auto*/
  min-width: /*0*/100%; /*if you want a % width, set it here, else set to 0*/
  height: /*188px*/100%; /*set table height here; can be fixed value or %*/
  min-height: /*0*/104px; /*if using % height, make this large enough to fit scrollbar arrows + caption + thead*/
  font-family: Verdana, Tahoma, sans-serif;
  font-size: 16px;
  line-height: 20px;
  padding: 20px 0 20px 0; /*need enough padding to make room for caption*/
  text-align: left;
}
.scrollingtable * {box-sizing: border-box;}
.scrollingtable > div {
  position: relative;
  border-top: 1px solid black;
  height: 100%;
  padding-top: 20px; /*this determines column header height*/
}
.scrollingtable > div:before {
  top: 0;
  background: cornflowerblue; /*header row background color*/
}
.scrollingtable > div:before,
.scrollingtable > div > div:after {
  content: "";
  position: absolute;
  z-index: -1;
  width: 100%;
  height: 100%;
  left: 0;
}
.scrollingtable > div > div {
  min-height: /*0*/43px; /*if using % height, make this large enough to fit scrollbar arrows*/
  max-height: 100%;
  overflow: /*scroll*/auto; /*set to auto if using fixed or % width; else scroll*/
  overflow-x: hidden;
  border: 1px solid black; /*border around table body*/
}
.scrollingtable > div > div:after {background: white;} /*match page background color*/
.scrollingtable > div > div > table {
  width: 100%;
  border-spacing: 0;
  margin-top: -20px; /*inverse of column header height*/
  margin-right: 17px; /*uncomment if using % width*/
}
.scrollingtable > div > div > table > caption {
  position: absolute;
  top: -20px; /*inverse of caption height*/
  margin-top: -1px; /*inverse of border-width*/
  width: 100%;
  font-weight: bold;
  text-align: center;
}
.scrollingtable > div > div > table > * > tr > * {padding: 0;}
.scrollingtable > div > div > table > thead {
  vertical-align: bottom;
  white-space: nowrap;
  text-align: center;
}
.scrollingtable > div > div > table > thead > tr > * > div {
  display: inline-block;
  padding: 0 6px 0 6px; /*header cell padding*/
}
.scrollingtable > div > div > table > thead > tr > :first-child:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 20px; /*match column header height*/
  border-left: 1px solid black; /*leftmost header border*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div > div:first-child,
.scrollingtable > div > div > table > thead > tr > * + :before {
  position: absolute;
  top: 0;
  white-space: pre-wrap;
  color: white; /*header row font color*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);}
.scrollingtable > div > div > table > thead > tr > * + :before {
  content: "";
  display: block;
  min-height: 20px; /*match column header height*/
  padding-top: 1px;
  border-left: 1px solid black; /*borders between header cells*/
}
.scrollingtable .scrollbarhead {float: right;}
.scrollingtable .scrollbarhead:before {
  position: absolute;
  width: 100px;
  top: -1px; /*inverse border-width*/
  background: white; /*match page background color*/
}
.scrollingtable > div > div > table > tbody > tr:after {
  content: "";
  display: table-cell;
  position: relative;
  padding: 0;
  border-top: 1px solid black;
  top: -1px; /*inverse of border width*/
}
.scrollingtable > div > div > table > tbody {vertical-align: top;}
.scrollingtable > div > div > table > tbody > tr {background: white;}
.scrollingtable > div > div > table > tbody > tr > * {
  border-bottom: 1px solid black;
  padding: 0 6px 0 6px;
  height: 20px; /*match column header height*/
}
.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;}
.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /*alternate row color*/
.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /*borders between body cells*/
</style>

The background colors will have to be changed and the borders removed for it to look exactly like the OP's table. Instructions for making those types of changes can be found in the CSS comments.

Check my answer here for an alternate configuration, and check d-Pixie's answer in the same post for an explanation of the basic technique used to freeze the header row.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM