原生javascript 固定表头原理与源码
我在工作中需要固定表头这个功能,我不想去找,没意思。于是就写了一个,我写的是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 固定表头原理与源码的更多相关文章
- JQuery固定表头插件fixedtableheader源码注释
在开发XX车站信息系统时,需要将大量数据显示在一个巨大的表格内部,由于表格是一个整体,无法分页,加之数据很多,超出一屏,为了方便用户,决定使用固定表头的插件,经过测试,发现JQuery 插件:fixe ...
- HashMap和ConcurrentHashMap实现原理及源码分析
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...
- 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析
在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...
- 第十一节、Harris角点检测原理(附源码)
OpenCV可以检测图像的主要特征,然后提取这些特征.使其成为图像描述符,这类似于人的眼睛和大脑.这些图像特征可作为图像搜索的数据库.此外,人们可以利用这些关键点将图像拼接起来,组成一个更大的图像,比 ...
- 【转】HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造
原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论 自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...
- 每天学会一点点(HashMap实现原理及源码分析)
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希 ...
- springmvc工作原理以及源码分析(基于spring3.1.0)
springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...
- 原生JS研究:学习jquery源码,收集整理常用JS函数
原生JS研究:学习jquery源码,收集整理常用JS函数: 1. JS获取原生class(getElementsByClass) 转自:http://blog.csdn.net/kongjiea/ar ...
随机推荐
- 【.net 深呼吸】序列化中的“引用保留”
假设 K 类中有两个属性/字段的类型相同,并且它们引用的是同一个对象实例,在序列化的默认处理中,会为每个引用单独生成数据. 看看下面两个类. [DataContract] public class 帅 ...
- 使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus
最新的Mono 4.4已经支持运行asp.net mvc5项目,有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目,然后部署到Mono上,浏览下发现一堆错 ...
- 算法笔记_013:汉诺塔问题(Java递归法和非递归法)
目录 1 问题描述 2 解决方案 2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...
- C# 正则表达式大全
文章导读 正则表达式的本质是使用一系列特殊字符模式,来表示某一类字符串.正则表达式无疑是处理文本最有力的工具,而.NET提供的Regex类实现了验证正则表达式的方法.Regex 类表示不可变(只读)的 ...
- javascript匹配各种括号书写是否正确
今天在codewars上做了一道题,如下 看上去就是验证三种括号各种嵌套是否正确书写,本来一头雾水,一种括号很容易判断, 但是三种怎么判断! 本人只是个前端菜鸟,,不会什么高深的正则之类的. 于是,在 ...
- .Net 大型分布式基础服务架构横向演变概述
一. 业务背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便于运维及监控. 二. 基础 ...
- UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件
在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...
- 深入理解javascript的getTime方法
1.理解getTime getTime() 方法返回一个时间的格林威治时间数值. 可以使用这个方法把一个日期时间赋值给另一个Date 对象. 语法: dateObj.getTime() 参数: 无. ...
- 工行ICBC_WAPB_B2C支付接口
一. 前期准备 手机银行(WAP)B2C在线支付接口说明V1.0.0.6.doc 手机银行移动生活商户及门户网站js接口API.doc 支付组件ICBCEBankUtil.dll和infosecapi ...
- Win7安装MySQL-5.7.16过程
1.在C盘新建MYSQL文件夹:2.将mysql-5.7.16-winx64拷贝到C:\MYSQL文件夹下,更名为mysql-5.7.16:3.在mysql-5.7.16目录下,建my.ini文件,内 ...