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

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

先讲一下思路:

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

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

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

    4.加scroll事件;

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

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

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
     <style>
         *{box-sizing: border-box;}
         .table{max-width: 100%; width: 100%;border-collapse: collapse;}
         .table>thead>tr>th{background-color: #059ca1; color:#FFF; padding-top:10px;padding-bottom: 10px;}
         .table>thead>tr>th,.table>tbody>tr>td{
             border:1px solid #CCC;
         }
     </style>
 </head>
 <body>
 <div style="width: 80%; margin:40px auto; height: 100px;overflow: auto;position: relative;">
     <table class="table">
         <thead>
         <tr>
             <th>Name1</th>
             <th>Name2</th>
             <th>Name3</th>
         </tr>
         </thead>
         <tbody>
         <tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr><tr>
             <td>亚瑟</td>
             <td>荆轲</td>
             <td>程咬金</td>
         </tr>
         </tbody>
     </table>
 </div>
 </body>
 </html>

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

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

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

 function FixHeader(tableElement){
     this.tableElement=tableElement;
 }

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

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

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

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

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

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

 cloneFixHeader:function(){
     this.cloneHeader.className='cloneThead';
     this.cloneHeader.style.position='absolute';
     this.cloneHeader.style.top=0;
     this.cloneHeader.style.left=0;
     this.cloneHeader.style.right='-1px';
     this.tableElement.appendChild(this.cloneHeader);
 }

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

运行的截图如下:

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

 cloneFixHeader:function(){
     var cloneThs=this.cloneHeader.children[0].children,
         ths=this.header.children[0].children,
         th,cloneTh,i=0,l=cloneThs.length;
     for(;i<l;i++){
         th=ths[i];cloneTh=cloneThs[i];
         cloneTh.style.width=th.offsetWidth+'px';
         cloneTh.style.height=th.offsetHeight+'px';
     }
     this.cloneHeader.className='cloneThead';
     this.cloneHeader.style.position='absolute';
     this.cloneHeader.style.top=0;
     this.cloneHeader.style.left=0;
     this.cloneHeader.style.right='-1px';
     this.tableElement.appendChild(this.cloneHeader);
 }

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

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

 listenerScroll:function(ev){
     var top=ev.target.scrollTop,
             //用于判断是否已经添加上了,添加了就不让再次添加
             cloneThead=ev.target.querySelector('.cloneThead');
     if(top>0){
         if(cloneThead){
             cloneThead.style.display='block';
             cloneThead.style.top=top+'px';
             return;
         }
         this.cloneFixHeader();
     }else{
         if(cloneThead){
             cloneThead.style.display='none';
         }
     }
 },

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

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

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

截图如下:

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

 listenerResize:function(){
     var that=this;
     if(that.timer){
         clearTimeout(that.timer);
     }
     that.timer=setTimeout(function(){
         var top=that.tableParent.scrollTop;
         if(top<=0){
             return;
         }
         var globalWidth=that.global.innerWidth;
         if(that.globalWidth&&that.globalWidth==globalWidth){
             return;
         }
         that.globalWidth=globalWidth;
         var cloneThead=that.tableElement.querySelector('.cloneThead'),
              theads=that.tableElement.querySelectorAll('thead'),i,l=theads.length;
         for(i=0;i<l;i++){
             if(theads[i].className!='cloneThead'){
                 that.header=theads[i];
                 break;
             }
         }
         if(cloneThead){
             var cloneThs=cloneThead.children[0].children,
                     ths=that.header.children[0].children,
                     th,cloneTh;
             l=cloneThs.length;
             for(i=0;i<l;i++){
                 th=ths[i];cloneTh=cloneThs[i];
                 cloneTh.style.width=th.offsetWidth+'px';
                 cloneTh.style.height=th.offsetHeight+'px';
             }
             return;
         }
         that.cloneFixHeader();
     },60);
 },

最后全部js代码如下:

 function FixHeader(tableElement, global) {
         this.tableElement = tableElement;
         this.global = global;
         this.timer = null;
     }
     FixHeader.prototype = {
         constructor: FixHeader,
         init: function () {
             this.tableParent = this.tableElement.parentNode;
             this.header = this.tableElement.querySelector('thead');
             this.cloneHeader = this.header.cloneNode(true);
             this.tableParent.addEventListener('scroll', this.listenerScroll.bind(this), false);
             this.global.addEventListener('resize', this.listenerResize.bind(this), false);
         },
         listenerScroll: function (ev) {
             var top = ev.target.scrollTop,
             //用于判断是否已经添加上了,添加了就不让再次添加
                     cloneThead = ev.target.querySelector('.cloneThead');
             if (top > 0) {
                 if (cloneThead) {
                     cloneThead.style.display = 'block';
                     cloneThead.style.top = top + 'px';
                     return;
                 }
                 this.cloneFixHeader();
             } else {
                 if (cloneThead) {
                     cloneThead.style.display = 'none';
                 }
             }
         },
         listenerResize: function () {
             var that = this;
             if (that.timer) {
                 clearTimeout(that.timer);
             }
             that.timer = setTimeout(function () {
                 var top = that.tableParent.scrollTop;
                 if (top <= 0) {
                     return;
                 }
                 var globalWidth = that.global.innerWidth;
                 if (that.globalWidth && that.globalWidth == globalWidth) {
                     return;
                 }
                 that.globalWidth = globalWidth;
                 var cloneThead = that.tableElement.querySelector('.cloneThead'),
                         theads = that.tableElement.querySelectorAll('thead'), i, l = theads.length;
                 for (i = 0; i < l; i++) {
                     if (theads[i].className != 'cloneThead') {
                         that.header = theads[i];
                         break;
                     }
                 }
                 if (cloneThead) {
                     var cloneThs = cloneThead.children[0].children,
                             ths = that.header.children[0].children,
                             th, cloneTh;
                     l = cloneThs.length;
                     for (i = 0; i < l; i++) {
                         th = ths[i];
                         cloneTh = cloneThs[i];
                         cloneTh.style.width = th.offsetWidth + 'px';
                         cloneTh.style.height = th.offsetHeight + 'px';
                     }
                     return;
                 }
                 that.cloneFixHeader();
             }, 60);
         },
         cloneFixHeader: function () {
             var cloneThs = this.cloneHeader.children[0].children,
                     ths = this.header.children[0].children,
                     th, cloneTh, i = 0, l = cloneThs.length;
             for (; i < l; i++) {
                 th = ths[i];
                 cloneTh = cloneThs[i];
                 cloneTh.style.width = th.offsetWidth + 'px';
                 cloneTh.style.height = th.offsetHeight + 'px';
             }
             this.cloneHeader.className = 'cloneThead';
             this.cloneHeader.style.position = 'absolute';
             this.cloneHeader.style.top = 0;
             this.cloneHeader.style.left = 0;
             this.cloneHeader.style.right = '-1px';
             this.tableElement.appendChild(this.cloneHeader);
         }
     };

调用方式如下:

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

总结:

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

原生javascript 固定表头原理与源码的更多相关文章

  1. JQuery固定表头插件fixedtableheader源码注释

    在开发XX车站信息系统时,需要将大量数据显示在一个巨大的表格内部,由于表格是一个整体,无法分页,加之数据很多,超出一屏,为了方便用户,决定使用固定表头的插件,经过测试,发现JQuery 插件:fixe ...

  2. HashMap和ConcurrentHashMap实现原理及源码分析

    HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...

  3. 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析

    在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...

  4. 第十一节、Harris角点检测原理(附源码)

    OpenCV可以检测图像的主要特征,然后提取这些特征.使其成为图像描述符,这类似于人的眼睛和大脑.这些图像特征可作为图像搜索的数据库.此外,人们可以利用这些关键点将图像拼接起来,组成一个更大的图像,比 ...

  5. 【转】HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  6. 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造

    原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论   自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...

  7. 每天学会一点点(HashMap实现原理及源码分析)

    HashMap实现原理及源码分析   哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希 ...

  8. springmvc工作原理以及源码分析(基于spring3.1.0)

    springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...

  9. 原生JS研究:学习jquery源码,收集整理常用JS函数

    原生JS研究:学习jquery源码,收集整理常用JS函数: 1. JS获取原生class(getElementsByClass) 转自:http://blog.csdn.net/kongjiea/ar ...

随机推荐

  1. mongo安全:增加用户名密码

    0.简述:在非auth下创建账户,然后重启 1.以不需要用户名密码的方式启动mongodb 2.运行客户端mongo,输入以下指令 show dbs;use admin;db.createRole({ ...

  2. IIS与Apache共用80端口方法

    IIS与Apache共用80端口 http://www.cnblogs.com/haocool/p/3595282.html Windows server 2003服务器上安装有默认 IIS 6和Ap ...

  3. 【BZOJ 1026】 [SCOI2009]windy数

    Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? In ...

  4. Linux学习之wget命令

    Linux系统中的wget是一个下载文件的工具,它用在命令行下.对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器.wget支持HTTP,HTTPS和FTP协 ...

  5. Gradle的一些技巧和遇到的问题

    全局变量的使用 在多个module的情况下,不同module的build.gradle文件中有部分配置项类似,或者依赖的类库,有部分是相同的,在维护上不是很方便,这个时候就可以考虑统一配置.在项目根目 ...

  6. [Ubuntu] 运行.AppImage格式文件

    右键Properties, Permissions勾选Allow executing file as program,如图

  7. 【转】Android中dip(dp)与px之间单位转换

    Android中dip(dp)与px之间单位转换 dp这个单位可能对web开发的人比较陌生,因为一般都是使用px(像素)但是,现在在开始android应用和游戏后,基本上都转换成用dp作用为单位了,因 ...

  8. linux 备份与恢复

  9. 20190316xlVba_设置行高的改进方案

    Public Sub AutoSetRowHeight(ByVal sht As Worksheet, Optional RowsInOnePage As Long) Dim BreakRow As ...

  10. Jsp (Java Server Pages)相关知识九大内置对象和四大作用域

    一.初识JSP Jsp页面的组成:静态内容.指令.表达式.小脚本.声明.标准动作.注释等元素构成 Url:统一资源定位符 Url组成:协议.主机名(包括端口号).路径 1.注释的方式: 1.HTML注 ...