是否有跨浏览器的CSS/JavaScript技术来显示一个长HTML表,使列标题保持固定在屏幕上,而不随表体滚动。想想微软Excel中的“冻结窗格”效果。

我希望能够滚动表的内容,但总是能够在顶部看到列标题。


当前回答

这不是固定标题行的精确解决方案,但我创建了一种相当巧妙的方法,在整个长表中重复标题行,但仍然保持排序能力。

这个简洁的小选项需要jQuery tablesorter插件。下面是它的工作原理:

HTML

<table class="tablesorter boxlist" id="pmtable">
    <thead class="fixedheader">
        <tr class="boxheadrow">
            <th width="70px" class="header">Job Number</th>
            <th width="10px" class="header">Pri</th>
            <th width="70px" class="header">CLLI</th>
            <th width="35px" class="header">Market</th>
            <th width="35px" class="header">Job Status</th>
            <th width="65px" class="header">Technology</th>
            <th width="95px;" class="header headerSortDown">MEI</th>
            <th width="95px" class="header">TEO Writer</th>
            <th width="75px" class="header">Quote Due</th>
            <th width="100px" class="header">Engineer</th>
            <th width="75px" class="header">ML Due</th>
            <th width="75px" class="header">ML Complete</th>
            <th width="75px" class="header">SPEC Due</th>
            <th width="75px" class="header">SPEC Complete</th>
            <th width="100px" class="header">Install Supervisor</th>
            <th width="75px" class="header">MasTec OJD</th>
            <th width="75px" class="header">Install Start</th>
            <th width="30px" class="header">Install Hours</th>
            <th width="75px" class="header">Revised CRCD</th>
            <th width="75px" class="header">Latest Ship-To-Site</th>
            <th width="30px" class="header">Total Parts</th>
            <th width="30px" class="header">OEM Rcvd</th>
            <th width="30px" class="header">Minor Rcvd</th>
            <th width="30px" class="header">Total Received</th>
            <th width="30px" class="header">% On Site</th>
            <th width="60px" class="header">Actions</th>
        </tr>
    </thead>
        <tbody class="scrollable">
            <tr data-job_id="3548" data-ml_id="" class="odd">
                <td class="c black">FL-8-RG9UP</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">FTLDFLOV</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">Chris Byrd</td>
                <td class="c">Apr 13, 2013</td>
                <td class="c">Kris Hall</td>
                <td class="c">May 20, 2013</td>
                <td class="c">May 20, 2013</td>
                <td class="c">Jun 5, 2013</td>
                <td class="c">Jun 7, 2013</td>
                <td class="c">Joseph Fitz</td>
                <td class="c">Jun 10, 2013</td>
                <td class="c">TBD</td>
                <td class="c">123</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Jul 26, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058616"></td>
                <td class="c">TBD</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span></td>
            </tr>
            <tr data-job_id="4264" data-ml_id="2959" class="even">
                <td class="c black">MTS13009SF</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">OJUSFLTL</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">DeMarcus Stewart</td>
                <td class="c">May 22, 2013</td>
                <td class="c">Ryan Alsobrook</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jun 27, 2013</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jul 4, 2013</td>
                <td class="c">Randy Williams</td>
                <td class="c">Jun 21, 2013</td>
                <td class="c">TBD</td>
                <td class="c">95</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Aug 9, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058632"></td><td class="c">TBD</td>
                <td class="c">0</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span><input style="float:left;" type="hidden" name="req_ship" class="reqShip hasDatepicker" id="dp1377194058464"><span style="float:left;" class="ui-icon ui-icon-calendar requestShip" title="Schedule this job for shipping"></span><span class="ui-icon ui-icon-info viewOrderInfo" style="float:left;" title="Show material details for this order"></span></td>
            </tr>
            .
            .
            .
            .
            <tr class="boxheadrow repeated-header">
                <th width="70px" class="header">Job Number</th>
                <th width="10px" class="header">Pri</th>
                <th width="70px" class="header">CLLI</th>
                <th width="35px" class="header">Market</th>
                <th width="35px" class="header">Job Status</th>
                <th width="65px" class="header">Technology</th>
                <th width="95px;" class="header">MEI</th>
                <th width="95px" class="header">TEO Writer</th>
                <th width="75px" class="header">Quote Due</th>
                <th width="100px" class="header">Engineer</th>
                <th width="75px" class="header">ML Due</th>
                <th width="75px" class="header">ML Complete</th>
                <th width="75px" class="header">SPEC Due</th>
                <th width="75px" class="header">SPEC Complete</th>
                <th width="100px" class="header">Install Supervisor</th>
                <th width="75px" class="header">MasTec OJD</th>
                <th width="75px" class="header">Install Start</th>
                <th width="30px" class="header">Install Hours</th>
                <th width="75px" class="header">Revised CRCD</th>
                <th width="75px" class="header">Latest Ship-To-Site</th>
                <th width="30px" class="header">Total Parts</th>
                <th width="30px" class="header">OEM Rcvd</th>
                <th width="30px" class="header">Minor Rcvd</th>
                <th width="30px" class="header">Total Received</th>
                <th width="30px" class="header">% On Site</th>
                <th width="60px" class="header">Actions</th>
            </tr>

显然,我的表的行要比这个多很多。确切地说是193,但是你可以看到标题行重复的地方。重复的标题行是由这个函数建立的:

jQuery

// Clone the original header row and add the "repeated-header" class
var tblHeader = $('tr.boxheadrow').clone().addClass('repeated-header');

// Add the cloned header with the new class every 34th row (or as you see fit)
$('tbody tr:odd:nth-of-type(17n)').after(tblHeader);

// On the 'sortStart' routine, remove all the inserted header rows
$('#pmtable').bind('sortStart', function() {
    $('.repeated-header').remove();
    // On the 'sortEnd' routine, add back all the header row lines.
}).bind('sortEnd', function() {
    $('tbody tr:odd:nth-of-type(17n)').after(tblHeader);
});

其他回答

这可以在四行代码中清晰地解决。

如果您只关心现代浏览器,那么使用CSS转换可以更容易地实现固定标头。听起来很奇怪,但效果很好:

HTML和CSS保持原样。 没有外部JavaScript依赖。 四行代码。 适用于所有配置(表布局:固定等)。

document.getElementById("wrap").addEventListener("scroll", function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});

除了Internet Explorer 8-,对CSS转换的支持广泛可用。

下面是完整的例子供参考:

document.getElementById("wrap").addEventListener("scroll",function(){ var translate = "translate(0,"+this.scrollTop+"px)"; this.querySelector("thead").style.transform = translate; }); /* Your existing container */ #wrap { overflow: auto; height: 400px; } /* CSS for demo */ td { background-color: green; width: 200px; height: 100px; } <div id="wrap"> <table> <thead> <tr> <th>Foo</th> <th>Bar</th> </tr> </thead> <tbody> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> </tbody> </table> </div>

这不是固定标题行的精确解决方案,但我创建了一种相当巧妙的方法,在整个长表中重复标题行,但仍然保持排序能力。

这个简洁的小选项需要jQuery tablesorter插件。下面是它的工作原理:

HTML

<table class="tablesorter boxlist" id="pmtable">
    <thead class="fixedheader">
        <tr class="boxheadrow">
            <th width="70px" class="header">Job Number</th>
            <th width="10px" class="header">Pri</th>
            <th width="70px" class="header">CLLI</th>
            <th width="35px" class="header">Market</th>
            <th width="35px" class="header">Job Status</th>
            <th width="65px" class="header">Technology</th>
            <th width="95px;" class="header headerSortDown">MEI</th>
            <th width="95px" class="header">TEO Writer</th>
            <th width="75px" class="header">Quote Due</th>
            <th width="100px" class="header">Engineer</th>
            <th width="75px" class="header">ML Due</th>
            <th width="75px" class="header">ML Complete</th>
            <th width="75px" class="header">SPEC Due</th>
            <th width="75px" class="header">SPEC Complete</th>
            <th width="100px" class="header">Install Supervisor</th>
            <th width="75px" class="header">MasTec OJD</th>
            <th width="75px" class="header">Install Start</th>
            <th width="30px" class="header">Install Hours</th>
            <th width="75px" class="header">Revised CRCD</th>
            <th width="75px" class="header">Latest Ship-To-Site</th>
            <th width="30px" class="header">Total Parts</th>
            <th width="30px" class="header">OEM Rcvd</th>
            <th width="30px" class="header">Minor Rcvd</th>
            <th width="30px" class="header">Total Received</th>
            <th width="30px" class="header">% On Site</th>
            <th width="60px" class="header">Actions</th>
        </tr>
    </thead>
        <tbody class="scrollable">
            <tr data-job_id="3548" data-ml_id="" class="odd">
                <td class="c black">FL-8-RG9UP</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">FTLDFLOV</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">Chris Byrd</td>
                <td class="c">Apr 13, 2013</td>
                <td class="c">Kris Hall</td>
                <td class="c">May 20, 2013</td>
                <td class="c">May 20, 2013</td>
                <td class="c">Jun 5, 2013</td>
                <td class="c">Jun 7, 2013</td>
                <td class="c">Joseph Fitz</td>
                <td class="c">Jun 10, 2013</td>
                <td class="c">TBD</td>
                <td class="c">123</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Jul 26, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058616"></td>
                <td class="c">TBD</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span></td>
            </tr>
            <tr data-job_id="4264" data-ml_id="2959" class="even">
                <td class="c black">MTS13009SF</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">OJUSFLTL</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">DeMarcus Stewart</td>
                <td class="c">May 22, 2013</td>
                <td class="c">Ryan Alsobrook</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jun 27, 2013</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jul 4, 2013</td>
                <td class="c">Randy Williams</td>
                <td class="c">Jun 21, 2013</td>
                <td class="c">TBD</td>
                <td class="c">95</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Aug 9, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058632"></td><td class="c">TBD</td>
                <td class="c">0</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span><input style="float:left;" type="hidden" name="req_ship" class="reqShip hasDatepicker" id="dp1377194058464"><span style="float:left;" class="ui-icon ui-icon-calendar requestShip" title="Schedule this job for shipping"></span><span class="ui-icon ui-icon-info viewOrderInfo" style="float:left;" title="Show material details for this order"></span></td>
            </tr>
            .
            .
            .
            .
            <tr class="boxheadrow repeated-header">
                <th width="70px" class="header">Job Number</th>
                <th width="10px" class="header">Pri</th>
                <th width="70px" class="header">CLLI</th>
                <th width="35px" class="header">Market</th>
                <th width="35px" class="header">Job Status</th>
                <th width="65px" class="header">Technology</th>
                <th width="95px;" class="header">MEI</th>
                <th width="95px" class="header">TEO Writer</th>
                <th width="75px" class="header">Quote Due</th>
                <th width="100px" class="header">Engineer</th>
                <th width="75px" class="header">ML Due</th>
                <th width="75px" class="header">ML Complete</th>
                <th width="75px" class="header">SPEC Due</th>
                <th width="75px" class="header">SPEC Complete</th>
                <th width="100px" class="header">Install Supervisor</th>
                <th width="75px" class="header">MasTec OJD</th>
                <th width="75px" class="header">Install Start</th>
                <th width="30px" class="header">Install Hours</th>
                <th width="75px" class="header">Revised CRCD</th>
                <th width="75px" class="header">Latest Ship-To-Site</th>
                <th width="30px" class="header">Total Parts</th>
                <th width="30px" class="header">OEM Rcvd</th>
                <th width="30px" class="header">Minor Rcvd</th>
                <th width="30px" class="header">Total Received</th>
                <th width="30px" class="header">% On Site</th>
                <th width="60px" class="header">Actions</th>
            </tr>

显然,我的表的行要比这个多很多。确切地说是193,但是你可以看到标题行重复的地方。重复的标题行是由这个函数建立的:

jQuery

// Clone the original header row and add the "repeated-header" class
var tblHeader = $('tr.boxheadrow').clone().addClass('repeated-header');

// Add the cloned header with the new class every 34th row (or as you see fit)
$('tbody tr:odd:nth-of-type(17n)').after(tblHeader);

// On the 'sortStart' routine, remove all the inserted header rows
$('#pmtable').bind('sortStart', function() {
    $('.repeated-header').remove();
    // On the 'sortEnd' routine, add back all the header row lines.
}).bind('sortEnd', function() {
    $('tbody tr:odd:nth-of-type(17n)').after(tblHeader);
});

简单的jQuery插件

这是Mahes解的一个变种。你可以像这样调用它$('table#foo').scrollableTable();

这个想法是:

将head和tbody分割为单独的表元素 使它们的单元格宽度再次匹配 将第二个表包装在div.scrollable中 使用CSS使div.scrollable实际滚动

CSS可以是:

div.scrollable { height: 300px; overflow-y: scroll;}

警告

显然,拆分这些表会降低标记的语义性。我不确定这对可访问性有什么影响。 这个插件不处理页脚,多个页眉等。 我只在Chrome 20版本中测试过。

也就是说,它符合我的目的,你可以自由地使用和修改它。

下面是插件:

jQuery.fn.scrollableTable = function () {
  var $newTable, $oldTable, $scrollableDiv, originalWidths;
  $oldTable = $(this);

  // Once the tables are split, their cell widths may change. 
  // Grab these so we can make the two tables match again.
  originalWidths = $oldTable.find('tr:first td').map(function() {
    return $(this).width();
  });

  $newTable = $oldTable.clone();
  $oldTable.find('tbody').remove();
  $newTable.find('thead').remove();

  $.each([$oldTable, $newTable], function(index, $table) {
    $table.find('tr:first td').each(function(i) {
      $(this).width(originalWidths[i]);
    });
  });

  $scrollableDiv = $('<div/>').addClass('scrollable');
  $newTable.insertAfter($oldTable).wrap($scrollableDiv);
};

博士TL;

如果您的目标是现代浏览器,并且没有奢侈的样式需求:http://jsfiddle.net/dPixie/byB9d/3/…虽然四大版本是相当甜蜜的以及这个版本处理流体宽度好得多。

大家好!

随着HTML5和CSS3的进步,至少对于现代浏览器来说,这是可能的。我提出的稍微有点粗糙的实现可以在这里找到:http://jsfiddle.net/dPixie/byB9d/3/。我已经在FX 25, Chrome 31和IE 10上测试过了…

相关的HTML(在你的文档顶部插入一个HTML5文档类型):

html, body { margin: 0; padding: 0; height: 100%; } section { position: relative; border: 1px solid #000; padding-top: 37px; background: #500; } section.positioned { position: absolute; top: 100px; left: 100px; width: 800px; box-shadow: 0 0 15px #333; } .container { overflow-y: auto; height: 200px; } table { border-spacing: 0; width: 100%; } td+td { border-left: 1px solid #eee; } td, th { border-bottom: 1px solid #eee; background: #ddd; color: #000; padding: 10px 25px; } th { height: 0; line-height: 0; padding-top: 0; padding-bottom: 0; color: transparent; border: none; white-space: nowrap; } th div { position: absolute; background: transparent; color: #fff; padding: 9px 25px; top: 0; margin-left: -25px; line-height: normal; border-left: 1px solid #800; } th:first-child div { border: none; } <section class="positioned"> <div class="container"> <table> <thead> <tr class="header"> <th> Table attribute name <div>Table attribute name</div> </th> <th> Value <div>Value</div> </th> <th> Description <div>Description</div> </th> </tr> </thead> <tbody> <tr> <td>align</td> <td>left, center, right</td> <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the alignment of a table according to surrounding text</td> </tr> <tr> <td>bgcolor</td> <td>rgb(x,x,x), #xxxxxx, colorname</td> <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the background color for a table</td> </tr> <tr> <td>border</td> <td>1,""</td> <td>Specifies whether the table cells should have borders or not</td> </tr> <tr> <td>cellpadding</td> <td>pixels</td> <td>Not supported in HTML5. Specifies the space between the cell wall and the cell content</td> </tr> <tr> <td>cellspacing</td> <td>pixels</td> <td>Not supported in HTML5. Specifies the space between cells</td> </tr> <tr> <td>frame</td> <td>void, above, below, hsides, lhs, rhs, vsides, box, border</td> <td>Not supported in HTML5. Specifies which parts of the outside borders that should be visible</td> </tr> <tr> <td>rules</td> <td>none, groups, rows, cols, all</td> <td>Not supported in HTML5. Specifies which parts of the inside borders that should be visible</td> </tr> <tr> <td>summary</td> <td>text</td> <td>Not supported in HTML5. Specifies a summary of the content of a table</td> </tr> <tr> <td>width</td> <td>pixels, %</td> <td>Not supported in HTML5. Specifies the width of a table</td> </tr> </tbody> </table> </div> </section>

但是如何? !

简单地说,你有一个表头,你通过使它高0px来隐藏它,它还包含了用作固定表头的div。表的容器在顶部留下了足够的空间来允许绝对定位的标题,并且带有滚动条的表如您所期望的那样出现。

上面的代码使用了定位类来确定表的位置(我在弹出式对话框中使用它),但是您也可以通过从容器中删除定位类来在文档流中使用它。

但是…

它并不完美。Firefox拒绝使标题行为0px(至少我没有找到任何方法),但固执地保持它至少为4px…这不是一个大问题,但取决于你的样式,它会混淆你的边界等。

该表还使用了一种伪列方法,其中容器本身的背景色被用作标题div的背景,这是透明的。

总结

总而言之,根据您的需求,可能会有样式问题,特别是边界或复杂的背景。可计算性可能也有问题,我还没有在各种各样的浏览器中检查它(如果你尝试过,请评论你的经验),但我没有发现任何类似的东西,所以我认为无论如何都值得发布…

通过将StickyTableHeaders jQuery插件应用到表格中,当你向下滚动时,列标题将粘在视口的顶部。

例子:

$(function () { $("table").stickyTableHeaders(); }); /*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders MIT license info: https://github.com/jmosbech/StickyTableHeaders/blob/master/license.txt */ ; (function ($, window, undefined) { 'use strict'; var name = 'stickyTableHeaders', id = 0, defaults = { fixedOffset: 0, leftOffset: 0, marginTop: 0, scrollableArea: window }; function Plugin(el, options) { // To avoid scope issues, use 'base' instead of 'this' // to reference this class from internal events and functions. var base = this; // Access to jQuery and DOM versions of element base.$el = $(el); base.el = el; base.id = id++; base.$window = $(window); base.$document = $(document); // Listen for destroyed, call teardown base.$el.bind('destroyed', $.proxy(base.teardown, base)); // Cache DOM refs for performance reasons base.$clonedHeader = null; base.$originalHeader = null; // Keep track of state base.isSticky = false; base.hasBeenSticky = false; base.leftOffset = null; base.topOffset = null; base.init = function () { base.$el.each(function () { var $this = $(this); // remove padding on <table> to fix issue #7 $this.css('padding', 0); base.$originalHeader = $('thead:first', this); base.$clonedHeader = base.$originalHeader.clone(); $this.trigger('clonedHeader.' + name, [base.$clonedHeader]); base.$clonedHeader.addClass('tableFloatingHeader'); base.$clonedHeader.css('display', 'none'); base.$originalHeader.addClass('tableFloatingHeaderOriginal'); base.$originalHeader.after(base.$clonedHeader); base.$printStyle = $('<style type="text/css" media="print">' + '.tableFloatingHeader{display:none !important;}' + '.tableFloatingHeaderOriginal{position:static !important;}' + '</style>'); $('head').append(base.$printStyle); }); base.setOptions(options); base.updateWidth(); base.toggleHeaders(); base.bind(); }; base.destroy = function () { base.$el.unbind('destroyed', base.teardown); base.teardown(); }; base.teardown = function () { if (base.isSticky) { base.$originalHeader.css('position', 'static'); } $.removeData(base.el, 'plugin_' + name); base.unbind(); base.$clonedHeader.remove(); base.$originalHeader.removeClass('tableFloatingHeaderOriginal'); base.$originalHeader.css('visibility', 'visible'); base.$printStyle.remove(); base.el = null; base.$el = null; }; base.bind = function () { base.$scrollableArea.on('scroll.' + name, base.toggleHeaders); if (!base.isWindowScrolling) { base.$window.on('scroll.' + name + base.id, base.setPositionValues); base.$window.on('resize.' + name + base.id, base.toggleHeaders); } base.$scrollableArea.on('resize.' + name, base.toggleHeaders); base.$scrollableArea.on('resize.' + name, base.updateWidth); }; base.unbind = function () { // unbind window events by specifying handle so we don't remove too much base.$scrollableArea.off('.' + name, base.toggleHeaders); if (!base.isWindowScrolling) { base.$window.off('.' + name + base.id, base.setPositionValues); base.$window.off('.' + name + base.id, base.toggleHeaders); } base.$scrollableArea.off('.' + name, base.updateWidth); }; base.toggleHeaders = function () { if (base.$el) { base.$el.each(function () { var $this = $(this), newLeft, newTopOffset = base.isWindowScrolling ? ( isNaN(base.options.fixedOffset) ? base.options.fixedOffset.outerHeight() : base.options.fixedOffset) : base.$scrollableArea.offset().top + (!isNaN(base.options.fixedOffset) ? base.options.fixedOffset : 0), offset = $this.offset(), scrollTop = base.$scrollableArea.scrollTop() + newTopOffset, scrollLeft = base.$scrollableArea.scrollLeft(), scrolledPastTop = base.isWindowScrolling ? scrollTop > offset.top : newTopOffset > offset.top, notScrolledPastBottom = (base.isWindowScrolling ? scrollTop : 0) < (offset.top + $this.height() - base.$clonedHeader.height() - (base.isWindowScrolling ? 0 : newTopOffset)); if (scrolledPastTop && notScrolledPastBottom) { newLeft = offset.left - scrollLeft + base.options.leftOffset; base.$originalHeader.css({ 'position': 'fixed', 'margin-top': base.options.marginTop, 'left': newLeft, 'z-index': 3 // #18: opacity bug }); base.leftOffset = newLeft; base.topOffset = newTopOffset; base.$clonedHeader.css('display', ''); if (!base.isSticky) { base.isSticky = true; // make sure the width is correct: the user might have resized the browser while in static mode base.updateWidth(); } base.setPositionValues(); } else if (base.isSticky) { base.$originalHeader.css('position', 'static'); base.$clonedHeader.css('display', 'none'); base.isSticky = false; base.resetWidth($('td,th', base.$clonedHeader), $('td,th', base.$originalHeader)); } }); } }; base.setPositionValues = function () { var winScrollTop = base.$window.scrollTop(), winScrollLeft = base.$window.scrollLeft(); if (!base.isSticky || winScrollTop < 0 || winScrollTop + base.$window.height() > base.$document.height() || winScrollLeft < 0 || winScrollLeft + base.$window.width() > base.$document.width()) { return; } base.$originalHeader.css({ 'top': base.topOffset - (base.isWindowScrolling ? 0 : winScrollTop), 'left': base.leftOffset - (base.isWindowScrolling ? 0 : winScrollLeft) }); }; base.updateWidth = function () { if (!base.isSticky) { return; } // Copy cell widths from clone if (!base.$originalHeaderCells) { base.$originalHeaderCells = $('th,td', base.$originalHeader); } if (!base.$clonedHeaderCells) { base.$clonedHeaderCells = $('th,td', base.$clonedHeader); } var cellWidths = base.getWidth(base.$clonedHeaderCells); base.setWidth(cellWidths, base.$clonedHeaderCells, base.$originalHeaderCells); // Copy row width from whole table base.$originalHeader.css('width', base.$clonedHeader.width()); }; base.getWidth = function ($clonedHeaders) { var widths = []; $clonedHeaders.each(function (index) { var width, $this = $(this); if ($this.css('box-sizing') === 'border-box') { width = $this[0].getBoundingClientRect().width; // #39: border-box bug } else { var $origTh = $('th', base.$originalHeader); if ($origTh.css('border-collapse') === 'collapse') { if (window.getComputedStyle) { width = parseFloat(window.getComputedStyle(this, null).width); } else { // ie8 only var leftPadding = parseFloat($this.css('padding-left')); var rightPadding = parseFloat($this.css('padding-right')); // Needs more investigation - this is assuming constant border around this cell and it's neighbours. var border = parseFloat($this.css('border-width')); width = $this.outerWidth() - leftPadding - rightPadding - border; } } else { width = $this.width(); } } widths[index] = width; }); return widths; }; base.setWidth = function (widths, $clonedHeaders, $origHeaders) { $clonedHeaders.each(function (index) { var width = widths[index]; $origHeaders.eq(index).css({ 'min-width': width, 'max-width': width }); }); }; base.resetWidth = function ($clonedHeaders, $origHeaders) { $clonedHeaders.each(function (index) { var $this = $(this); $origHeaders.eq(index).css({ 'min-width': $this.css('min-width'), 'max-width': $this.css('max-width') }); }); }; base.setOptions = function (options) { base.options = $.extend({}, defaults, options); base.$scrollableArea = $(base.options.scrollableArea); base.isWindowScrolling = base.$scrollableArea[0] === window; }; base.updateOptions = function (options) { base.setOptions(options); // scrollableArea might have changed base.unbind(); base.bind(); base.updateWidth(); base.toggleHeaders(); }; // Run initializer base.init(); } // A plugin wrapper around the constructor, // preventing against multiple instantiations $.fn[name] = function (options) { return this.each(function () { var instance = $.data(this, 'plugin_' + name); if (instance) { if (typeof options === 'string') { instance[options].apply(instance); } else { instance.updateOptions(options); } } else if (options !== 'destroy') { $.data(this, 'plugin_' + name, new Plugin(this, options)); } }); }; })(jQuery, window); body { margin: 0 auto; padding: 0 20px; font-family: Arial, Helvetica, sans-serif; font-size: 11px; color: #555; } table { border: 0; padding: 0; margin: 0 0 20px 0; border-collapse: collapse; } th { padding: 5px; /* NOTE: th padding must be set explicitly in order to support IE */ text-align: right; font-weight:bold; line-height: 2em; color: #FFF; background-color: #555; } tbody td { padding: 10px; line-height: 18px; border-top: 1px solid #E0E0E0; } tbody tr:nth-child(2n) { background-color: #F7F7F7; } tbody tr:hover { background-color: #EEEEEE; } td { text-align: right; } td:first-child, th:first-child { text-align: left; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <div style="width:3000px">some really really wide content goes here</div> <table> <thead> <tr> <th colspan="9">Companies listed on NASDAQ OMX Copenhagen.</th> </tr> <tr> <th>Full name</th> <th>CCY</th> <th>Last</th> <th>+/-</th> <th>%</th> <th>Bid</th> <th>Ask</th> <th>Volume</th> <th>Turnover</th> </tr> </thead> <tbody> <tr> <td>A.P. Møller...</td> <td>DKK</td> <td>33,220.00</td> <td>760</td> <td>2.34</td> <td>33,140.00</td> <td>33,220.00</td> <td>594</td> <td>19,791,910</td> </tr> <tr> <td>A.P. Møller...</td> <td>DKK</td> <td>34,620.00</td> <td>640</td> <td>1.88</td> <td>34,620.00</td> <td>34,700.00</td> <td>9,954</td> <td>346,530,246</td> </tr> <tr> <td>Carlsberg A</td> <td>DKK</td> <td>380</td> <td>0</td> <td>0</td> <td>371</td> <td>391.5</td> <td>6</td> <td>2,280</td> </tr> <tr> <td>Carlsberg B</td> <td>DKK</td> <td>364.4</td> <td>8.6</td> <td>2.42</td> <td>363</td> <td>364.4</td> <td>636,267</td> <td>228,530,601</td> </tr> <tr> <td>Chr. Hansen...</td> <td>DKK</td> <td>114.5</td> <td>-1.6</td> <td>-1.38</td> <td>114.2</td> <td>114.5</td> <td>141,822</td> <td>16,311,454</td> </tr> <tr> <td>Coloplast B</td> <td>DKK</td> <td>809.5</td> <td>11</td> <td>1.38</td> <td>809</td> <td>809.5</td> <td>85,840</td> <td>69,363,301</td> </tr> <tr> <td>D/S Norden</td> <td>DKK</td> <td>155</td> <td>-1.5</td> <td>-0.96</td> <td>155</td> <td>155.1</td> <td>51,681</td> <td>8,037,225</td> </tr> <tr> <td>Danske Bank</td> <td>DKK</td> <td>69.05</td> <td>2.55</td> <td>3.83</td> <td>69.05</td> <td>69.2</td> <td>1,723,719</td> <td>115,348,068</td> </tr> <tr> <td>DSV</td> <td>DKK</td> <td>105.4</td> <td>0.2</td> <td>0.19</td> <td>105.2</td> <td>105.4</td> <td>674,873</td> <td>71,575,035</td> </tr> <tr> <td>FLSmidth &amp; Co.</td> <td>DKK</td> <td>295.8</td> <td>-1.8</td> <td>-0.6</td> <td>295.1</td> <td>295.8</td> <td>341,263</td> <td>100,301,032</td> </tr> <tr> <td>G4S plc</td> <td>DKK</td> <td>22.53</td> <td>0.05</td> <td>0.22</td> <td>22.53</td> <td>22.57</td> <td>190,920</td> <td>4,338,150</td> </tr> <tr> <td>Jyske Bank</td> <td>DKK</td> <td>144.2</td> <td>1.4</td> <td>0.98</td> <td>142.8</td> <td>144.2</td> <td>78,163</td> <td>11,104,874</td> </tr> <tr> <td>Københavns ...</td> <td>DKK</td> <td>1,580.00</td> <td>-12</td> <td>-0.75</td> <td>1,590.00</td> <td>1,620.00</td> <td>82</td> <td>131,110</td> </tr> <tr> <td>Lundbeck</td> <td>DKK</td> <td>103.4</td> <td>-2.5</td> <td>-2.36</td> <td>103.4</td> <td>103.8</td> <td>157,162</td> <td>16,462,282</td> </tr> <tr> <td>Nordea Bank</td> <td>DKK</td> <td>43.22</td> <td>-0.06</td> <td>-0.14</td> <td>43.22</td> <td>43.25</td> <td>167,520</td> <td>7,310,143</td> </tr> <tr> <td>Novo Nordisk B</td> <td>DKK</td> <td>552.5</td> <td>-3.5</td> <td>-0.63</td> <td>550.5</td> <td>552.5</td> <td>843,533</td> <td>463,962,375</td> </tr> <tr> <td>Novozymes B</td> <td>DKK</td> <td>805.5</td> <td>5.5</td> <td>0.69</td> <td>805</td> <td>805.5</td> <td>152,188</td> <td>121,746,199</td> </tr> <tr> <td>Pandora</td> <td>DKK</td> <td>39.04</td> <td>0.94</td> <td>2.47</td> <td>38.8</td> <td>39.04</td> <td>350,965</td> <td>13,611,838</td> </tr> <tr> <td>Rockwool In...</td> <td>DKK</td> <td>492</td> <td>0</td> <td>0</td> <td>482</td> <td>492</td> <td></td> <td></td> </tr> <tr> <td>Rockwool In...</td> <td>DKK</td> <td>468</td> <td>12</td> <td>2.63</td> <td>465.2</td> <td>468</td> <td>9,885</td> <td>4,623,850</td> </tr> <tr> <td>Sydbank</td> <td>DKK</td> <td>95</td> <td>0.05</td> <td>0.05</td> <td>94.7</td> <td>95</td> <td>103,438</td> <td>9,802,899</td> </tr> <tr> <td>TDC</td> <td>DKK</td> <td>43.6</td> <td>0.13</td> <td>0.3</td> <td>43.5</td> <td>43.6</td> <td>845,110</td> <td>36,785,339</td> </tr> <tr> <td>Topdanmark</td> <td>DKK</td> <td>854</td> <td>13.5</td> <td>1.61</td> <td>854</td> <td>855</td> <td>38,679</td> <td>32,737,678</td> </tr> <tr> <td>Tryg</td> <td>DKK</td> <td>290.4</td> <td>0.3</td> <td>0.1</td> <td>290</td> <td>290.4</td> <td>94,587</td> <td>27,537,247</td> </tr> <tr> <td>Vestas Wind...</td> <td>DKK</td> <td>90.15</td> <td>-4.2</td> <td>-4.45</td> <td>90.1</td> <td>90.15</td> <td>1,317,313</td> <td>121,064,314</td> </tr> <tr> <td>William Dem...</td> <td>DKK</td> <td>417.6</td> <td>0.1</td> <td>0.02</td> <td>417</td> <td>417.6</td> <td>64,242</td> <td>26,859,554</td> </tr> </tbody> </table> <div style="height: 4000px">lots of content down here...</div>