我在工作中需要固定表头这个功能,我不想去找,没意思。于是就写了一个,我写的是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="width: 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();

总结:

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

原生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. 防御XSS攻击-encode用户输入内容的重要性

    一.开场先科普下XSS 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶 ...

  2. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  3. SQL Server 致程序员(容易忽略的错误)

    标签:SQL SERVER/MSSQL/DBA/T-SQL好习惯/数据库/需要注意的地方/程序员/容易犯的错误/遇到的问题 概述 因为每天需要审核程序员发布的SQL语句,所以收集了一些程序员的一些常见 ...

  4. hibernate多对多关联映射

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  5. HTML5 语义元素(一)页面结构

    本篇主要介绍HTML5增加的语义元素中关于页面结构方面的,包含: <article>.<aside>.<figure>.<figcaption>.< ...

  6. WPF做12306验证码点击效果

    一.效果 和12306是一样的,运行一张图上点击多个位置,横线以上和左边框还有有边框位置不允许点击,点击按钮输出坐标集合,也就是12306登陆的时候,需要向后台传递的参数. 二.实现思路 1.获取验证 ...

  7. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  8. AbpZero--1.如何开始

    1.加群 群号:104390185,下载这个文件并解压 用VS2015打开aspnet-zero-1.9.0.1 2.修改Web项目web.config连接字符串 <add name=" ...

  9. 太多选择——企业如何选择合适的BI工具?

    在没认清现状前,企业当然不能一言不合就上BI. BI不同于一般的企业管理软件,不能简单归类为类似用于提高管理的ERP和WMS,或用于提高企业效率的OA.BPM.BI的本质应该是通过展现数据,用于加强企 ...

  10. 不要着急改代码,先想想--centos 6.8下编译安装tmux

    诸位读者新年好,2017开年第一篇博客,请允许我先问候一下看到这篇博客的诸位.写博客是我2017年定下的目标之一,希望我会坚持下去. 最近打算尝试一下tmux这个神器,于是有了这一篇关于思维方式的Bl ...