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

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


当前回答

我开发了一个简单的轻量级jQuery插件,用于将格式良好的HTML表转换为具有固定表头和列的可滚动表。

该插件可以很好地匹配固定部分与可滚动部分的像素到像素的定位。此外,您还可以冻结水平滚动时始终显示在视图中的列数。

演示和文档:http://meetselva.github.io/fixed-table-rows-cols/

GitHub存储库:https://github.com/meetselva/fixed-table-rows-cols

下面是带有固定表头的简单表的用法,

$(<table selector>).fxdHdrCol({
    width:     "100%",
    height:    200,
    colModal: [{width: 30, align: 'center'},
               {width: 70, align: 'center'}, 
               {width: 200, align: 'left'}, 
               {width: 100, align: 'center'}, 
               {width: 70, align: 'center'}, 
               {width: 250, align: 'center'}
              ]
});

其他回答

支持固定页脚

我扩展了内森的功能,也支持一个固定的页脚和最大高度。 此外,该函数将设置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();
};

我希望我能早点找到@Mark的解决方案,但在我看到这个SO问题之前,我自己写了一个……

我的是一个非常轻量级的jQuery插件,它支持固定的页眉、页脚、列跨越(colspan)、调整大小、水平滚动,以及滚动开始前可选的显示行数。

jQuery。scrollTableBody (GitHub)

只要你有一个合适的<thead>, <tbody>,和(可选)<tfoot>的表,你所需要做的就是:

$('table').scrollTableBody();

我喜欢Maximillian Hils的回答,但我有一些问题:

这个变换在Edge或IE中不起作用,除非你把它应用到第th项上 在Edge和IE中滚动时,标题会闪烁 我的表是使用ajax加载的,所以我想附加到窗口滚动事件,而不是包装器的滚动事件

为了摆脱闪烁,我使用一个超时等待,直到用户完成滚动,然后我应用转换-所以在滚动期间头是不可见的。

我还使用jQuery编写了这篇文章,其优点之一是jQuery可以为您处理供应商前缀

    var isScrolling, lastTop, lastLeft, isLeftHidden, isTopHidden;

    //Scroll events don't bubble https://stackoverflow.com/a/19375645/150342
    //so can't use $(document).on("scroll", ".table-container-fixed", function (e) {
    document.addEventListener('scroll', function (event) {
        var $container = $(event.target);
        if (!$container.hasClass("table-container-fixed"))
            return;    

        //transform needs to be applied to th for Edge and IE
        //in this example I am also fixing the leftmost column
        var $topLeftCell = $container.find('table:first > thead > tr > th:first');
        var $headerCells = $topLeftCell.siblings();
        var $columnCells = $container
           .find('table:first > tbody > tr > td:first-child, ' +
                 'table:first > tfoot > tr > td:first-child');

        //hide the cells while returning otherwise they show on top of the data
        if (!isLeftHidden) {
            var currentLeft = $container.scrollLeft();
            if (currentLeft < lastLeft) {
                //scrolling left
                isLeftHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $columnCells.css('visibility', 'hidden');
            }
            lastLeft = currentLeft;
        }

        if (!isTopHidden) {
            var currentTop = $container.scrollTop();
            if (currentTop < lastTop) {
                //scrolling up
                isTopHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $headerCells.css('visibility', 'hidden');
            }
            lastTop = currentTop;
        }

        // Using timeout to delay transform until user stops scrolling
        // Clear timeout while scrolling
        window.clearTimeout(isScrolling);

        // Set a timeout to run after scrolling ends
        isScrolling = setTimeout(function () {
            //move the table cells. 
            var x = $container.scrollLeft();
            var y = $container.scrollTop();

            $topLeftCell.css('transform', 'translate(' + x + 'px, ' + y + 'px)');
            $headerCells.css('transform', 'translateY(' + y + 'px)');
            $columnCells.css('transform', 'translateX(' + x + 'px)');

            isTopHidden = isLeftHidden = false;
            $topLeftCell.css('visibility', 'inherit');
            $headerCells.css('visibility', 'inherit');
            $columnCells.css('visibility', 'inherit');
        }, 100);

    }, true);

表被包装在一个div中,类table-container-fixed。

.table-container-fixed{
    overflow: auto;
    height: 400px;
}

我将border-collapse设置为分开,否则在翻译过程中就会丢失边界,并且我删除了表上的边界,以防止内容在滚动过程中刚好出现在边界所在的单元格上方。

.table-container-fixed > table {
   border-collapse: separate;
   border:none;
}

我将th背景设置为白色以覆盖下面的单元格,并添加了与表格边框相匹配的边框——使用Bootstrap样式并滚动出视图。

 .table-container-fixed > table > thead > tr > th {
        border-top: 1px solid #ddd !important;
        background-color: white;        
        z-index: 10;
        position: relative;/*to make z-index work*/
    }

            .table-container-fixed > table > thead > tr > th:first-child {
                z-index: 20;
            }

.table-container-fixed > table > tbody > tr > td:first-child,
.table-container-fixed > table > tfoot > tr > td:first-child {
    background-color: white;        
    z-index: 10;
    position: relative;
}

以下是对马克西米利安·希尔斯(Maximilian Hils)发布的答案的改进。

这个在ie11中没有任何闪烁:

var headerCells = tableWrap.querySelectorAll("thead td");
for (var i = 0; i < headerCells.length; i++) {
    var headerCell = headerCells[i];
    headerCell.style.backgroundColor = "silver";
}
var lastSTop = tableWrap.scrollTop;
tableWrap.addEventListener("scroll", function () {
    var stop = this.scrollTop;
    if (stop < lastSTop) {
        // Resetting the transform for the scrolling up to hide the headers
        for (var i = 0; i < headerCells.length; i++) {
            headerCells[i].style.transitionDelay = "0s";
            headerCells[i].style.transform = "";
        }
    }
    lastSTop = stop;
    var translate = "translate(0," + stop + "px)";
    for (var i = 0; i < headerCells.length; i++) {
        headerCells[i].style.transitionDelay = "0.25s";
        headerCells[i].style.transform = translate;
    }
});

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

这个简洁的小选项需要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);
});