zoukankan      html  css  js  c++  java
  • 原生javascript 固定表头原理与源码

        我在工作中需要固定表头这个功能,我不想去找,没意思。于是就写了一个,我写的是angularjs 自定义指令 起了个 "fix-header" ,有人叫  “freeze-header” ,算了,看心情吧,最近心情不太好就不改了~~~

    想了想,我还是改成原生吧,angularjs就是个毛毛~~~。

    先讲一下思路:

        1.想一想,再想一想,肯定用定位了,具体是绝对定位还是固定定位,看实际情况;

        2.clone 一份thead元素,用于再创建一个定位的表头;

        3.clone有点坑,不能clone当前元素的 实际 宽高 和 事件, 只能获取原先的加上;

        4.加scroll事件;

        5.我很开心,成功了~~~~;

    先把页面创建了 ,就叫fixHeaderDemo.html,如下:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6     <style>
     7         *{box-sizing: border-box;}
     8         .table{max-width: 100%; width: 100%;border-collapse: collapse;}
     9         .table>thead>tr>th{background-color: #059ca1; color:#FFF; padding-top:10px;padding-bottom: 10px;}
    10         .table>thead>tr>th,.table>tbody>tr>td{
    11             border:1px solid #CCC;
    12         }
    13     </style>
    14 </head>
    15 <body>
    16 <div style=" 80%; margin:40px auto; height: 100px;overflow: auto;position: relative;">
    17     <table class="table">
    18         <thead>
    19         <tr>
    20             <th>Name1</th>
    21             <th>Name2</th>
    22             <th>Name3</th>
    23         </tr>
    24         </thead>
    25         <tbody>
    26         <tr>
    27             <td>亚瑟</td>
    28             <td>荆轲</td>
    29             <td>程咬金</td>
    30         </tr><tr>
    31             <td>亚瑟</td>
    32             <td>荆轲</td>
    33             <td>程咬金</td>
    34         </tr><tr>
    35             <td>亚瑟</td>
    36             <td>荆轲</td>
    37             <td>程咬金</td>
    38         </tr><tr>
    39             <td>亚瑟</td>
    40             <td>荆轲</td>
    41             <td>程咬金</td>
    42         </tr><tr>
    43             <td>亚瑟</td>
    44             <td>荆轲</td>
    45             <td>程咬金</td>
    46         </tr><tr>
    47             <td>亚瑟</td>
    48             <td>荆轲</td>
    49             <td>程咬金</td>
    50         </tr><tr>
    51             <td>亚瑟</td>
    52             <td>荆轲</td>
    53             <td>程咬金</td>
    54         </tr><tr>
    55             <td>亚瑟</td>
    56             <td>荆轲</td>
    57             <td>程咬金</td>
    58         </tr><tr>
    59             <td>亚瑟</td>
    60             <td>荆轲</td>
    61             <td>程咬金</td>
    62         </tr><tr>
    63             <td>亚瑟</td>
    64             <td>荆轲</td>
    65             <td>程咬金</td>
    66         </tr><tr>
    67             <td>亚瑟</td>
    68             <td>荆轲</td>
    69             <td>程咬金</td>
    70         </tr><tr>
    71             <td>亚瑟</td>
    72             <td>荆轲</td>
    73             <td>程咬金</td>
    74         </tr><tr>
    75             <td>亚瑟</td>
    76             <td>荆轲</td>
    77             <td>程咬金</td>
    78         </tr>
    79         </tbody>
    80     </table>
    81 </div>
    82 </body>
    83 </html>

    上面的都太小儿科了,js才是关键。

    其实真的很简单,有兴趣还能在优化:

    第一步,先来个构造函数,这个构造函数接收一个参数,也就是你要固定的那个表格,代码如下:

    1 function FixHeader(tableElement){
    2     this.tableElement=tableElement;
    3 }

    第二步,写FixHeader构造函数的方法:

    1 FixHeader.prototype={
    2     constructor:FixHeader,
    3     init:function(){
    4       //这个位置是初始化的位置      
    5     }
    6 };

    第三步,其实我们的滚动是表格外面有个div父级元素,设置了他的最大高度,当超过这个最大高度就显示滚动条。那么,我们在初始化函数中肯定先获取div和表头元素等一些初始的事情;

    1  init:function(){
    2     //获取表格的父级元素
    3     this.tableParent=this.tableElement.parentNode;
    4     //获取thead元素
    5     this.header=this.tableElement.querySelector('thead');
    6     //克隆thead元素
    7     this.cloneHeader=this.header.cloneNode(true);
    8  }

    第四步,我们要用克隆的数据,往表格中插入一个固定的表头,可能会问为什么要clone一下呢?因为如果直接操作原来的表头数据会直接影响,我们不是要去动原来的东西,那些东西已经完美了;如果你有兴趣可以

          尝试一下,你会收获很大。在FixHeader原型中加了一个cloneFixHeader函数

    1 cloneFixHeader:function(){
    2     this.cloneHeader.className='cloneThead';
    3     this.cloneHeader.style.position='absolute';
    4     this.cloneHeader.style.top=0;
    5     this.cloneHeader.style.left=0;
    6     this.cloneHeader.style.right='-1px';
    7     this.tableElement.appendChild(this.cloneHeader);
    8 }

    上面的代码大家肯定明白,就是给clone的元素加一些样式和属性,加一个className是为了标识它是克隆的。那先看看效果,在初始化函数中调用cloneFixHeader()函数;

    运行的截图如下:

    我的天哪,这是怎么回事,怎么这个熊样了。哈哈。。。上面我已经说了clone不能把原来的宽高和事件一起克隆了,有些人是在css中把每个表格的宽度都写死,这是多么不好的做法,每次加或者减一列,都要修改css中的宽度。那我们是怎么解决的呢?太简单了,把每个元素的宽高设置到clone的元素中不就可以了吗!

     1 cloneFixHeader:function(){
     2     var cloneThs=this.cloneHeader.children[0].children,
     3         ths=this.header.children[0].children,
     4         th,cloneTh,i=0,l=cloneThs.length;
     5     for(;i<l;i++){
     6         th=ths[i];cloneTh=cloneThs[i];
     7         cloneTh.style.width=th.offsetWidth+'px';
     8         cloneTh.style.height=th.offsetHeight+'px';
     9     }
    10     this.cloneHeader.className='cloneThead';
    11     this.cloneHeader.style.position='absolute';
    12     this.cloneHeader.style.top=0;
    13     this.cloneHeader.style.left=0;
    14     this.cloneHeader.style.right='-1px';
    15     this.tableElement.appendChild(this.cloneHeader);
    16 }

    运行的结果如下(太完美了~~~):

    第五步,应该监听滚动事件了。先在原型中加一个函数叫listenerScroll ,代码如下:

     1 listenerScroll:function(ev){
     2     var top=ev.target.scrollTop,
     3             //用于判断是否已经添加上了,添加了就不让再次添加
     4             cloneThead=ev.target.querySelector('.cloneThead');
     5     if(top>0){
     6         if(cloneThead){
     7             cloneThead.style.display='block';
     8             cloneThead.style.top=top+'px';
     9             return;
    10         }
    11         this.cloneFixHeader();
    12     }else{
    13         if(cloneThead){
    14             cloneThead.style.display='none';
    15         }
    16     }
    17 },

    把在init中调用的cloneFixHeader() 函数换成监听事件:

    1 init:function(){
    2     this.tableParent=this.tableElement.parentNode;
    3     this.header=this.tableElement.querySelector('thead');
    4     this.cloneHeader=this.header.cloneNode(true);
    5     this.tableParent.addEventListener('scroll',this.listenerScroll.bind(this),false);
    6 },

    上面看似完美了,但是当你改变浏览器窗口大小时,你会惊讶于我是多么认真与细心,是的,当窗口变化时,一切都不完美了,原因你应该知道的呀!

    截图如下:

    亲,你想到了方法吗?是的,就是监听窗口大小变化,好了再加一个listenerResize函数:

     1 listenerResize:function(){
     2     var that=this;
     3     if(that.timer){
     4         clearTimeout(that.timer);
     5     }
     6     that.timer=setTimeout(function(){
     7         var top=that.tableParent.scrollTop;
     8         if(top<=0){
     9             return;
    10         }
    11         var globalWidth=that.global.innerWidth;
    12         if(that.globalWidth&&that.globalWidth==globalWidth){
    13             return;
    14         }
    15         that.globalWidth=globalWidth;
    16         var cloneThead=that.tableElement.querySelector('.cloneThead'),
    17              theads=that.tableElement.querySelectorAll('thead'),i,l=theads.length;
    18         for(i=0;i<l;i++){
    19             if(theads[i].className!='cloneThead'){
    20                 that.header=theads[i];
    21                 break;
    22             }
    23         }
    24         if(cloneThead){
    25             var cloneThs=cloneThead.children[0].children,
    26                     ths=that.header.children[0].children,
    27                     th,cloneTh;
    28             l=cloneThs.length;
    29             for(i=0;i<l;i++){
    30                 th=ths[i];cloneTh=cloneThs[i];
    31                 cloneTh.style.width=th.offsetWidth+'px';
    32                 cloneTh.style.height=th.offsetHeight+'px';
    33             }
    34             return;
    35         }
    36         that.cloneFixHeader();
    37     },60);
    38 },

    最后全部js代码如下:

     1 function FixHeader(tableElement, global) {
     2         this.tableElement = tableElement;
     3         this.global = global;
     4         this.timer = null;
     5     }
     6     FixHeader.prototype = {
     7         constructor: FixHeader,
     8         init: function () {
     9             this.tableParent = this.tableElement.parentNode;
    10             this.header = this.tableElement.querySelector('thead');
    11             this.cloneHeader = this.header.cloneNode(true);
    12             this.tableParent.addEventListener('scroll', this.listenerScroll.bind(this), false);
    13             this.global.addEventListener('resize', this.listenerResize.bind(this), false);
    14         },
    15         listenerScroll: function (ev) {
    16             var top = ev.target.scrollTop,
    17             //用于判断是否已经添加上了,添加了就不让再次添加
    18                     cloneThead = ev.target.querySelector('.cloneThead');
    19             if (top > 0) {
    20                 if (cloneThead) {
    21                     cloneThead.style.display = 'block';
    22                     cloneThead.style.top = top + 'px';
    23                     return;
    24                 }
    25                 this.cloneFixHeader();
    26             } else {
    27                 if (cloneThead) {
    28                     cloneThead.style.display = 'none';
    29                 }
    30             }
    31         },
    32         listenerResize: function () {
    33             var that = this;
    34             if (that.timer) {
    35                 clearTimeout(that.timer);
    36             }
    37             that.timer = setTimeout(function () {
    38                 var top = that.tableParent.scrollTop;
    39                 if (top <= 0) {
    40                     return;
    41                 }
    42                 var globalWidth = that.global.innerWidth;
    43                 if (that.globalWidth && that.globalWidth == globalWidth) {
    44                     return;
    45                 }
    46                 that.globalWidth = globalWidth;
    47                 var cloneThead = that.tableElement.querySelector('.cloneThead'),
    48                         theads = that.tableElement.querySelectorAll('thead'), i, l = theads.length;
    49                 for (i = 0; i < l; i++) {
    50                     if (theads[i].className != 'cloneThead') {
    51                         that.header = theads[i];
    52                         break;
    53                     }
    54                 }
    55                 if (cloneThead) {
    56                     var cloneThs = cloneThead.children[0].children,
    57                             ths = that.header.children[0].children,
    58                             th, cloneTh;
    59                     l = cloneThs.length;
    60                     for (i = 0; i < l; i++) {
    61                         th = ths[i];
    62                         cloneTh = cloneThs[i];
    63                         cloneTh.style.width = th.offsetWidth + 'px';
    64                         cloneTh.style.height = th.offsetHeight + 'px';
    65                     }
    66                     return;
    67                 }
    68                 that.cloneFixHeader();
    69             }, 60);
    70         },
    71         cloneFixHeader: function () {
    72             var cloneThs = this.cloneHeader.children[0].children,
    73                     ths = this.header.children[0].children,
    74                     th, cloneTh, i = 0, l = cloneThs.length;
    75             for (; i < l; i++) {
    76                 th = ths[i];
    77                 cloneTh = cloneThs[i];
    78                 cloneTh.style.width = th.offsetWidth + 'px';
    79                 cloneTh.style.height = th.offsetHeight + 'px';
    80             }
    81             this.cloneHeader.className = 'cloneThead';
    82             this.cloneHeader.style.position = 'absolute';
    83             this.cloneHeader.style.top = 0;
    84             this.cloneHeader.style.left = 0;
    85             this.cloneHeader.style.right = '-1px';
    86             this.tableElement.appendChild(this.cloneHeader);
    87         }
    88     };

    调用方式如下:

    1  new FixHeader(document.querySelector('.table'), window).init();

    总结:

    表头固定可以用了,不过由于我知识有限,可能上面有错误的地方,请大家批评指出。

  • 相关阅读:
    Linux 文件查找
    Linux-Varnish缓存
    Linux-部署Hadoop环境
    Linux-部署MFS分布式文件系统
    Linux-部署Jumpserver跳板机
    Linux-部署Heartbeat高可用
    Linux-Mysql5.7之字段约束,索引,外键
    Linux-Mysql常用命令(下)
    Linux-Mysql常用命令(上)
    Python-常用模块详解
  • 原文地址:https://www.cnblogs.com/zhangkunweb/p/6142251.html
Copyright © 2011-2022 走看看