使用jQuery使表格标题在用户滚动时保持固定在顶部。

175
我尝试设计一个 HTML 表格,在用户滚动时,表头将保持在页面顶部,只有当用户滚动它超出视图时才会保留。例如,该表格可能距离页面下方 500 像素,如何使其在用户滚动表头超出视图时(浏览器以某种方式检测到它不再在窗口视图中),其保持在顶部?谁能给我提供一个 JavaScript 的解决方案?

<table>
  <thead>
    <tr>
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>info</td>
      <td>info</td>
      <td>info</td>
    </tr>
    <tr>
      <td>info</td>
      <td>info</td>
      <td>info</td>
    </tr>
    <tr>
      <td>info</td>
      <td>info</td>
      <td>info</td>
    </tr>
  </tbody>
</table>

在上面的例子中,如果超出视图范围,我希望它随页面滚动。
重要提示:我想要有滚动条(overflow:auto)的解决方案。

2
重复?https://dev59.com/lHNA5IYBdhLWcg3wQ7i4 - Brad Tofel
可能是HTML表格标题始终可见,当查看大型表格时的重复问题。 - Swastik Raj Ghosh
27个回答

3
以下代码完美运行,请尝试!

<style>
table th {
  background-color:gray;
  color:#fff;
}
table td {
  text-align:center;
  color:#000;
}
</style>
<table style='width:100%;'>
<tr style="position: -webkit-sticky; position: sticky; top: 0;">
  <th>Hour</th>
  <th>Start</th>
  <th>End</th>
  <th>Pcs</th>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>555</td>
  <td>666</td>
  <td>777</td>
  <td>888</td>
</tr>
<tr>
  <td>999</td>
  <td>1010</td>
  <td>1212</td>
  <td>1414</td>
</tr>
<tr>
  <td>1515</td>
  <td>1616</td>
  <td>1717</td>
  <td>1818</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
<tr>
  <td>111</td>
  <td>222</td>
  <td>333</td>
  <td>444</td>
</tr>
</table>


3
你可以使用这种方法,纯HTML和CSS无需JS :)

.table-fixed-header {
  display: flex;
  justify-content: space-between;
  margin-right: 18px
}

.table-fixed {
  display: flex;
  justify-content: space-between;
  height: 150px;
  overflow: scroll;
}

.column {
  flex-basis: 24%;
  border-radius: 5px;
  padding: 5px;
  text-align: center;
}
.column .title {
  border-bottom: 2px grey solid;
  border-top: 2px grey solid;
  text-align: center;
  display: block;
  font-weight: bold;
}

.cell {
  padding: 5px;
  border-right: 1px solid;
  border-left: 1px solid;
}

.cell:nth-of-type(even) {
  background-color: lightgrey;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Fixed header Bin</title>
</head>
<body>
<div class="table-fixed-header">
    
    <div class="column">
      <span class="title">col 1</span>
    </div>
    <div class="column">
      <span class="title">col 2</span>
    </div>
    <div class="column">
      <span class="title">col 3</span>
    </div>
    <div class="column">
      <span class="title">col 4</span>
    </div>
    
  </div>
  
  <div class="table-fixed">
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">beta</div>
      <div class="cell">beta</div>
      <div class="cell">beta</div>
      
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
  </div>
</body>
</html>


2
这里是一种基于已接受答案的解决方案,它修正了列宽、匹配表格样式以及当表格在容器div中滚动时的问题。
用法:
请确保你的表格有一个标签,因为只有thead内容会被固定。
$("#header-fixed").fixHeader();

JavaSript

//Custom JQuery Plugin
(function ($) {
    $.fn.fixHeader = function () {
        return this.each(function () {
            var $table = $(this);
            var $sp = $table.scrollParent();
            var tableOffset = $table.position().top;
            var $tableFixed = $("<table />")
                .prop('class', $table.prop('class'))
                .css({ position: "fixed", "table-layout": "fixed", display: "none", "margin-top": "0px" });
            $table.before($tableFixed);
            $tableFixed.append($table.find("thead").clone());

            $sp.bind("scroll", function () {
                var offset = $(this).scrollTop();

                if (offset > tableOffset && $tableFixed.is(":hidden")) {
                    $tableFixed.show();
                    var p = $table.position();
                    var offset = $sp.offset();

                    //Set the left and width to match the source table and the top to match the scroll parent
                    $tableFixed.css({ left: p.left + "px", top: (offset ? offset.top : 0) + "px", }).width($table.width());

                    //Set the width of each column to match the source table
                    $.each($table.find('th, td'), function (i, th) {
                        $($tableFixed.find('th, td')[i]).width($(th).width());
                    });

                }
                else if (offset <= tableOffset && !$tableFixed.is(":hidden")) {
                    $tableFixed.hide();
                }
            });
        });
    };
})(jQuery);

1
这对我很有效。唯一的问题是需要水平滚动的表格。我在JSFiddle中进行了更新,链接为http://jsfiddle.net/4mgneob1/1/。我减去了`$sp.scrollLeft()`,它获取从左侧滚动的量,因此当向下滚动时,固定表格的位置正确。还添加了一个检查,以确定在水平滚动时是否可见固定表格以调整左侧位置。另一个我无法解决的问题是当表格超出视口时,固定表格应该隐藏,直到用户再次向上滚动。 - Henesnarfel
1
出现“Uncaught TypeError: $table.scrollParent is not a function”错误 - karlingen

2
我也遇到了与entrophy的代码一样的边框格式问题,但是通过一些小修补,现在表格可以扩展并显示您可能添加的所有CSS样式规则。
要添加到CSS中:
#maintable{width: 100%}    

那么这里是新的JavaScript代码:

    function moveScroll(){
    var scroll = $(window).scrollTop();
    var anchor_top = $("#maintable").offset().top;
    var anchor_bottom = $("#bottom_anchor").offset().top;
    if (scroll > anchor_top && scroll < anchor_bottom) {
        clone_table = $("#clone");
        if(clone_table.length === 0) {          
            clone_table = $("#maintable").clone();
            clone_table.attr({id: "clone"})
            .css({
                position: "fixed",
                "pointer-events": "none",
                 top:0
            })
            .width($("#maintable").width());

            $("#table-container").append(clone_table);
            // dont hide the whole table or you lose border style & 
            // actively match the inline width to the #maintable width if the 
            // container holding the table (window, iframe, div) changes width          
            $("#clone").width($("#maintable").width());
            // only the clone thead remains visible
            $("#clone thead").css({
                visibility:"visible"
            });
            // clone tbody is hidden
            $("#clone tbody").css({
                visibility:"hidden"
            });
            // add support for a tfoot element
            // and hide its cloned version too
            var footEl = $("#clone tfoot");
            if(footEl.length){
                footEl.css({
                    visibility:"hidden"
                });
            }
        }
    } 
    else {
        $("#clone").remove();
    }
}
$(window).scroll(moveScroll);

这个方案在标题的边框格式方面比熵的解决方案要好。然而,在当前的Firefox浏览器中(但不是在Chrome浏览器中),一旦开始滚动,它会在表格下方创建可见的单元格边框伪影。 - alanng
这个解决方案是有效的。但当窗口变小时,克隆的标题不会水平滚动,有什么建议可以修复吗? - Nathalie

1
在这个解决方案中,动态创建了固定的标题,内容和样式是从THEAD克隆而来。
你只需要两行代码,例如:
var $myfixedHeader = $("#Ttodo").FixedHeader(); //创建固定的标题 $(window).scroll($myfixedHeader.moveScroll); //将函数绑定到滚动事件
我的jquery插件FixedHeader和getStyleObject如下,你可以放在.js文件中。

// JAVASCRIPT



/*
 * getStyleObject Plugin for jQuery JavaScript Library
 * From: http://upshots.org/?p=112
 
Basic usage:
$.fn.copyCSS = function(source){
  var styles = $(source).getStyleObject();
  this.css(styles);
}
*/

(function($){
    $.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            };
            style = window.getComputedStyle(dom, null);
            for(var i = 0, l = style.length; i < l; i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            };
            return returns;
        };
        if(style = dom.currentStyle){
            for(var prop in style){
                returns[prop] = style[prop];
            };
            return returns;
        };
        return this.css();
    }
})(jQuery);



   
//Floating Header of long table  PiotrC
(function ( $ ) {
    var tableTop,tableBottom,ClnH;
    $.fn.FixedHeader = function(){
        tableTop=this.offset().top,
        tableBottom=this.outerHeight()+tableTop;
        //Add Fixed header
        this.after('<table id="fixH"></table>');
        //Clone Header
        ClnH=$("#fixH").html(this.find("thead").clone());
        //set style
        ClnH.css({'position':'fixed', 'top':'0', 'zIndex':'60', 'display':'none',
        'border-collapse': this.css('border-collapse'),
  'border-spacing': this.css('border-spacing'),
        'margin-left': this.css('margin-left'),
        'width': this.css('width')            
        });
        //rewrite style cell of header
        $.each(this.find("thead>tr>th"), function(ind,val){
            $(ClnH.find('tr>th')[ind]).css($(val).getStyleObject());
        });
    return ClnH;}
    
    $.fn.moveScroll=function(){
        var offset = $(window).scrollTop();
        if (offset > tableTop && offset<tableBottom){
            if(ClnH.is(":hidden"))ClnH.show();
            $("#fixH").css('margin-left',"-"+$(window).scrollLeft()+"px");
        }
        else if (offset < tableTop || offset>tableBottom){
            if(!ClnH.is(':hidden'))ClnH.hide();
        }
    };
})( jQuery );





var $myfixedHeader = $("#repTb").FixedHeader();
$(window).scroll($myfixedHeader.moveScroll);
/* CSS - important only NOT transparent background */

#repTB{border-collapse: separate;border-spacing: 0;}

#repTb thead,#fixH thead{background: #e0e0e0 linear-gradient(#d8d8d8 0%, #e0e0e0 25%, #e0e0e0 75%, #d8d8d8 100%) repeat scroll 0 0;border:1px solid #CCCCCC;}

#repTb td{border:1px solid black}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


<h3>example</h3> 
<table id="repTb">
<thead>
<tr><th>Col1</th><th>Column2</th><th>Description</th></tr>
</thead>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
</table>


1

div.wrapper {
    padding:20px;
}
table.scroll thead {
    width: 100%;
    background: #FC6822;
}
table.scroll thead tr:after {
    content: '';
    overflow-y: scroll;
    visibility: hidden;
}
table.scroll thead th {
    flex: 1 auto;
    display: block;
    color: #fff;
}
table.scroll tbody {
    display: block;
    width: 100%;
    overflow-y: auto;
    height: auto;
    max-height: 200px;
}
table.scroll thead tr,
table.scroll tbody tr {
    display: flex;
}
table.scroll tbody tr td {
    flex: 1 auto;
    word-wrap: break;
}
table.scroll thead tr th,
table.scroll tbody tr td {
    width: 25%;
    padding: 5px;
    text-align-left;
    border-bottom: 1px solid rgba(0,0,0,0.3);
}
<div class="wrapper">
    <table border="0" cellpadding="0" cellspacing="0" class="scroll">
        <thead>
            <tr>
                <th>Name</th>
                <th>Vorname</th>
                <th>Beruf</th>
                <th>Alter</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
        </tbody>
    </table>
</div>

演示: css固定表头演示


1
使用这个来解决你的问题。
tbody {
  display: table-caption;
  height: 200px;
  caption-side: bottom;
  overflow: auto;
}

1
function fix_table_header_position(){
 var width_list = [];
 $("th").each(function(){
    width_list.push($(this).width());
 });
 $("tr:first").css("position", "absolute");
 $("tr:first").css("z-index", "1000");
 $("th, td").each(function(index){
    $(this).width(width_list[index]);
 });

 $("tr:first").after("<tr height=" + $("tr:first").height() + "></tr>");}

这是我的解决方案。

1
稍晚了一些,但这里提供了一个可以在同一页上处理多个表格且无"卡顿"问题的实现(使用requestAnimationFrame)。同时,列宽度无需指定。水平滚动也可以正常工作。
表头由
定义,因此您可以自由添加任何标记(如按钮),如果需要的话。这是所需的全部HTML代码:
<div class="tbl-resp">
  <table id="tbl1" class="tbl-resp__tbl">
     <thead>
      <tr>
        <th>col 1</th>
        <th>col 2</th>
        <th>col 3</th>
      </tr>
    </thead> 
  </table>
</div>

https://jsfiddle.net/lloydleo/bk5pt5gs/


1
在我的情况下,我只使用了CSS来解决这个问题:
首先,我们需要像这样的表格格式:
<table>
<thead><tr><th></th></tr></thead>
<tbody><tr><td></td><tr></tbody>
</table>

并应用此CSS:

tbody {
    display:block;
    max-height:280px;
    overflow-x: hidden;
    overflow-y: auto;
    }
    thead, tbody tr {
        display:table;
        width:100%;
        table-layout:fixed;
    }

并且标题栏是固定的


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接