表头固定,表身滚动的特效,被广泛运用于各种大数据量展示的页面,本人在实践开发过程中总结了3中实现方式,现在,分享给大家。

  • 思路一
      当发生滚动事件后,动态复制一份表头,将其放置在原表头的位置,为滚动的容器添加scroll事件,时刻保持复制表头的top=滚动条的垂直位置($(obj).scrollTop())
    以下为demo代码实现:

-CSS 实现

<style type="text/css">
.table {
width:240px;
}
.table tr >td,.table >thead>tr>th {
border:1px solid green;
width:50px;
}
.header-copy{
background:red;
}
#scrollContainer{
width:260px;
position:fixed;
height:200px;
overflow-y:auto;
}
</style

-HEML实现

<div id='scrollContainer'  >
    <table class="table" cellspacing=0 cellpadding=0 id="mytab">
        <thead >
            <tr>
                <th>姓名</th><th>爱好</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>码妞一</td><td>吉他</td>
            </tr>
        </tbody>
    </table>
</div>
<!-- 以下的js只是为了批量生成数据,真正固定表头的方法是fixTabHeader -->
<script type="text/javascript">
$(document).ready(function(){
    var row=$("#mytab >tbody>tr:first");
    for(i=0;i<30;i++){
        $('table#mytab > tbody').append(row.clone()); 
    }
    fixTabHeader("mytab","scrollContainer");
 });
</script>

-JS实现

<script language="javascript" type="text/javascript" >
 function fixTabHeader(tabid,scrollid){
        // 获取滚动条容器  
        var scrollDiv =$("#"+scrollid);
            // 将表格拷贝一份  
        var tb2 =$("#"+tabid).clone(true);  
            // 将拷贝得到的表格中非表头行删除  
        if(tb2.size()>0) {
            tb2.find("tr:not(:first)").remove();
        }
            // 创建一个div ,并添加到滚动容器中
        var bakDiv =$("<div style='position:absolute;display:none;width:100%;'></div>");
        bakDiv.css("top",scrollDiv.scrollTop());
        bakDiv.appendTo(scrollDiv);
            // 将拷贝得到的表格在删除数据行后添加到创建的div中  
        tb2.addClass("header-copy");
        tb2.appendTo(bakDiv);  
        scrollDiv.scroll(function(){
            // 设置div的top值为滚动条距离滚动条容器顶部的距离值  
            bakDiv.css({top:$(this).scrollTop()+"px",background:"#e8eaeb",display:"block"});
        });
    }
</script>
  • 分析
       以上实现的是基于某个父容器进行滚动的,如果有朋友想要实现基于浏览器进行滚动,只需要将fixTabHeader方法中scrollDiv 申明为当前文档对象就可以了,即scrollDiv=$(window).
      此种方法适合比较简单的表格,如果你的表格应用了很多外部样式,比如许多朋友为了效果好实用bootstrap框架做特效,这时候会发现该种方法实现的固头,会有表头的数据行和表内容无法对齐的现象,而且这个问题及其难调,本人花了好几天也没有搞定,如果有看文章的朋友解决了,欢迎和我交流。
  • 思路二
      实现思路同上面的demo相似,都是复制一份表头来做固定,不同的是上面例子是复制了一份表头直接放置到滚动容器中固定,而本例子是将复制的表头放在了tbody中,然后做固定。位置不同。
  • css
table {
width:98%;
text-align:center;
color:blue
}
table .header{
background:green;
}
table .header-fixed {
  position: fixed;
}
#scrollContainer{
height:200px;
overflow-y:auto;
}
<div id='scrollContainer'  >
<table class="table" cellspacing=0 cellpadding=0 id="mytable">
    <thead class="header">
            <tr>
                <th>姓名</th><th>爱好</th>
            </tr>
    </thead>
        <tbody>
            <tr>
                <td>一介码妞</td><td>吉他</td>
            </tr>
        </tbody>
</table>
</div>
<!-- 以下的js只是为了批量生成数据,真正固定表头的方法是fixTabHeader -->
<script type="text/javascript">
$(document).ready(function(){
    var row=$("#mytable >tbody>tr:first");
    for(i=0;i<80;i++){
        $('table#mytable > tbody').append(row.clone()); 
    }
     $("#mytable").fixedHeader();
 });
</script>
<script type="text/javascript">
(function () {
    (function ($) {
        return $.fn.fixedHeader = function (options) {
            var config;
            config = {
                bgColor: '#ff82ab'
            };
            if (options) {
                $.extend(config, options);
            }
            return this.each(function () {
                var $head, $parent, headTop, isFixed, o, ww;
                processScroll = function () {
                    var headTop, i, isFixed, scrollTop, t;
                    if (!o.is(':visible')) {
                        return;
                    }
                    i = void 0;
                    scrollTop = $parent.scrollTop();
                  t= $head.length && $head.offset().top;
                    if (!isFixed && headTop !== t) {
                        headTop = t;
                    }
                    if (scrollTop >= headTop && !isFixed) {
                        isFixed = 1;
                    } else {
                        if (scrollTop <= headTop && isFixed) {
                            isFixed = 0;
                        }
                    }
                    if (isFixed) {
                        return $('thead.header-copy', o).show();
                    } else {
                        return $('thead.header-copy', o).hide();
                    }
                };
                o = $(this);
                $parent=$("#scrollContainer");//$($(this).parent());
                $head = $('thead.header', o);
                isFixed = 0;
                headTop = ($head.length) && ($head.offset().top);
                $parent.on('scroll',processScroll);
                $head.on('click', function () {
                    if (!isFixed) {
                        return setTimeout(function () {
                            return $parent.scrollTop($head.scrollTop());
                        }, 10);
                    }
                });
                $head.clone().removeClass('header').addClass('header-copy header-fixed').appendTo(o);
                ww = [];
                o.find('thead.header > tr:first > th').each(function (i, h) {
                    return ww.push($(h).outerWidth(false));
                });
                $.each(ww, function (i, w) {
                    return o.find('thead.header > tr > th:eq(' + i + '), thead.header-copy > tr > th:eq(' + i + ')').css({ width: w });
                });
                o.find('thead.header-copy').css({
                    margin: '0 auto',
                    width: o.outerWidth(),
                    top:headTop-2,
                    'background-color': config.bgColor
                });
                return processScroll();
            });
        };
    }(jQuery));
}.call(this));
</script>
  • 分析
      demo1 和demo2的思路都是复制一份表头做固定。不同的是demo1有点粗糙直接放到了滚动容器中,做位置固定。而demo2是将它放到了自己table的tbody中。而且demo2还将表格固定的这个例子做成了一个比较通用的插件,方便在其他项目中通用。但是,令人遗憾的是,对于较复杂的例子,demo2也存在表头和表内容无法完全垂直的问题。而且不太好调试。
  • 思路三
      写两个表格,一个为表头,另一个为表内容。当表内容数据量比较大时,可以直接在表内容所在的父容器进行滚动。以下为demo代码实现:
<table>
    <thead class="header">
                <tr>
                    <th>姓名</th><th>爱好</th>
                </tr>
    </thead>
</table>
<div id='scrollContainer' style="height:300px;overflow-y:auto;" >
<table class="table" cellspacing=0 cellpadding=0 id="mytable">
        <tbody>
            <tr>
                <td>一介码妞</td><td>吉他</td>
            </tr>
        </tbody>
</table>
</div>
<!-- 以下的js只是为了批量生成数据,真正固定表头的方法是fixTabHeader -->
<script type="text/javascript">
$(document).ready(function(){
    var row=$("#mytable >tbody>tr:first");
    for(i=0;i<80;i++){
        $('table#mytable > tbody').append(row.clone()); 
    }
     $("#mytable").fixedHeader();
 });
</script>
<script type="text/javascript">
function fixTabHeaderScroll(tabid){
    var $parent,$head,pTop,oTop,headTop,o;
        o=$("#"+tabid);
        oTop=o.height();
        $parent=o.parent();
        pTop=$parent.height();
        $head=$parent.prev();
        if (pTop <=oTop) {

        $head.css({width:$head.outerWidth(true)-scrollBarWidth+"px"});

        }else{
            $head.css({width:100+"%"});
        }
}
</script>
  • 分析   这种方式应该是最简单,最明了的实现方式,表头和表内容分家,各管各家,对于表头来说,不管表内容滚或者不滚,它都在哪里不离不弃。但是存在一个问题,在表内容没有滚动时,表头和表身是垂直对齐的,一旦表身出现滚动,新出现的滚动条会占用表身的部分宽度,这就导致表身和表头又不对齐。所以,我们需要在给两个表格应用同样样式的基础上,再用js函数控制两个的宽度,使他们宽度一致,大体思路是获取滚动条的宽,然后设置表头的宽度减去滚动条的宽度。 补充介绍Jquery的几个参数: offsetWidth :当前对象的的宽度包括滚动条在内 scrollWidth :当前对象的滚动宽度不包括滚动条在内 在css的盒子模型中,最内部是content,然后是padding、border、margin
width=content 
 innerWidth = width + padding 
 outerWidth = innerWidth + border 
 outerWidth(true) = outerWidth + margin

  • 关于outerWidth属性有一个传入参数可以控制它包不包含外部的margin 为true的话包含,默认不包含。
    注意:要想表格相对于某个父容器做数据滚动,则该父容器必须做如下设置:
    (1)设置固定高度,不设置高度或者用百分比设置高度,滚动不起作用
    (2)设置属性overflow-y:auto
     必选项,否则当表格数据过多时,不会产生滚动条,而是自动延长该父容器的高度
    (3)设置 position:relative,这个主要是针对前连个例子而言
     若不设置,拷贝得来的表头将相对于其设置该属性为该值的父节点(或间接父节点)定位,如果没有,则相对于body