是否有跨浏览器的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);
});

其他回答

派对来的很晚了,但这仍然是一个派对,以下是我对顺风车的看法:

<div class="h-screen overflow-hidden flex flex-col">
  <div class="overflow-y-scroll flex-1">
    <table>
      <thead class="sticky top-0">
        <tr>
          <th>Timestamp</th>
          <th>Species</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>2022-02-09T08:20:39.967Z</td>
          <td>willow</td>
        </tr>
        <tr>
          <td>2022-02-09T08:21:29.453Z</td>
          <td>red osier dogwood</td>
        </tr>
        <tr>
          <td>2022-02-09T08:22:18.984Z</td>
          <td>buttonbush</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

以下是一个关于顺风游戏的完整工作示例。

这是一个用于固定表头的jQuery插件。它允许整个页面滚动,当它到达顶部时冻结标题。它与Twitter Bootstrap表一起工作得很好。

GitHub存储库:https://github.com/oma/table-fixed-header

它不只是滚动表内容。看看其他的工具,就像这些答案中的一个。你来决定最适合你的情况。

使用最新版本的jQuery,并包含以下JavaScript代码。

$(window).scroll(function(){
  $("id of the div element").offset({top:$(window).scrollTop()});
});

我一直在寻找一个解决方案,发现大多数答案都不工作或不适合我的情况,所以我用jQuery编写了一个简单的解决方案。

这是解决方案大纲:

克隆需要固定表头的表,并放置 在原拷贝之上的克隆拷贝。 从主表上拆下表体。 从底部表中删除表头。 调整列的宽度。(我们跟踪原始列的宽度)

下面是一个可运行的演示代码。

function scrolify(tblAsJQueryObject, height) { var oTbl = tblAsJQueryObject; // for very large tables you can remove the four lines below // and wrap the table with <div> in the mark-up and assign // height and overflow property var oTblDiv = $("<div/>"); oTblDiv.css('height', height); oTblDiv.css('overflow', 'scroll'); oTbl.wrap(oTblDiv); // save original width oTbl.attr("data-item-original-width", oTbl.width()); oTbl.find('thead tr td').each(function() { $(this).attr("data-item-original-width", $(this).width()); }); oTbl.find('tbody tr:eq(0) td').each(function() { $(this).attr("data-item-original-width", $(this).width()); }); // clone the original table var newTbl = oTbl.clone(); // remove table header from original table oTbl.find('thead tr').remove(); // remove table body from new table newTbl.find('tbody tr').remove(); oTbl.parent().parent().prepend(newTbl); newTbl.wrap("<div/>"); // replace ORIGINAL COLUMN width newTbl.width(newTbl.attr('data-item-original-width')); newTbl.find('thead tr td').each(function() { $(this).width($(this).attr("data-item-original-width")); }); oTbl.width(oTbl.attr('data-item-original-width')); oTbl.find('tbody tr:eq(0) td').each(function() { $(this).width($(this).attr("data-item-original-width")); }); } $(document).ready(function() { scrolify($('#tblNeedsScrolling'), 160); // 160 is height }); <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <div style="width:300px;border:6px green solid;"> <table border="1" width="100%" id="tblNeedsScrolling"> <thead> <tr><th>Header 1</th><th>Header 2</th></tr> </thead> <tbody> <tr><td>row 1, cell 1</td><td>row 1, cell 2</td></tr> <tr><td>row 2, cell 1</td><td>row 2, cell 2</td></tr> <tr><td>row 3, cell 1</td><td>row 3, cell 2</td></tr> <tr><td>row 4, cell 1</td><td>row 4, cell 2</td></tr> <tr><td>row 5, cell 1</td><td>row 5, cell 2</td></tr> <tr><td>row 6, cell 1</td><td>row 6, cell 2</td></tr> <tr><td>row 7, cell 1</td><td>row 7, cell 2</td></tr> <tr><td>row 8, cell 1</td><td>row 8, cell 2</td></tr> </tbody> </table> </div>

该解决方案支持Chrome和IE浏览器。因为它是基于jQuery的,所以在其他jQuery支持的浏览器中也能正常工作。

支持固定页脚

我扩展了内森的功能,也支持一个固定的页脚和最大高度。 此外,该函数将设置CSS本身,您只需要支持宽度。

用法:

固定高度:

$('table').scrollableTable({ height: 100 });

最大高度(如果浏览器支持CSS 'max-height'选项):

$('table').scrollableTable({ maxHeight: 100 });

脚本:

jQuery.fn.scrollableTable = function(options) {

    var $originalTable, $headTable, $bodyTable, $footTable, $scrollableDiv, originalWidths;

    // Prepare the separate parts of the table
    $originalTable = $(this);
    $headTable = $originalTable.clone();

    $headTable.find('tbody').remove();
    $headTable.find('tfoot').remove();

    $bodyTable = $originalTable.clone();
    $bodyTable.find('thead').remove();
    $bodyTable.find('tfoot').remove();

    $footTable = $originalTable.clone();
    $footTable.find('thead').remove();
    $footTable.find('tbody').remove();

    // Grab the original column widths and set them in the separate tables
    originalWidths = $originalTable.find('tr:first td').map(function() {
        return $(this).width();
    });

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

    // The div that makes the body table scroll
    $scrollableDiv = $('<div/>').css({
        'overflow-y': 'scroll'
    });

    if(options.height) {
        $scrollableDiv.css({'height': options.height});
    }
    else if(options.maxHeight) {
        $scrollableDiv.css({'max-height': options.maxHeight});
    }

    // Add the new separate tables and remove the original one
    $headTable.insertAfter($originalTable);
    $bodyTable.insertAfter($headTable);
    $footTable.insertAfter($bodyTable);
    $bodyTable.wrap($scrollableDiv);
    $originalTable.remove();
};