作者:cloudgamer 时间: 2009-09-17 文档类型:原创 来自:蓝色理想
第 1 页 JavaScript Table行定位效果 [1] 第 2 页 JavaScript Table行定位效果 [2] 第 3 页 JavaScript Table行定位效果 [3] 第 4 页 JavaScript Table行定位效果 [4] 第 5 页 JavaScript Table行定位效果 [5] 第 6 页 JavaScript Table行定位效果 [6] 第 7 页 JavaScript Table行定位效果 [7] 第 8 页 JavaScript Table行定位效果 [8]
近来有客户要求用table显示一大串数据,由于滚动后就看不到表头,很不方便,所以想到这个效果。上次做 table排序 对table有了一些了解,这次更是深入了解了一番,发现table原来是这么不简单。
还不清楚这个效果叫什么,有点像表头固定的效果,就叫行定位吧,本来想把列定位也做出来,但暂时还没这个需求,等以后有时间再弄吧。在 淘宝的商品搜索页 也看到类似的效果,但淘宝的不是table,而是li,而我这个是用在table上的。
要说明一下的是,我这个效果是用在一些普通的产品列表,当数据比较多时提高用户体验,而不是单单做数据显示,跟excel那样的方式是不同的。
效果预览
为方便预览,建议缩小浏览器。
运行代码框<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>JavaScript Table行定位效果</title> <script> var isIE = (document.all) ? true : false; var isIE6 = isIE && (navigator.userAgent.indexOf('MSIE 6.0') != -1); var isIE7 = isIE && (navigator.userAgent.indexOf('MSIE 7.0') != -1); var isIE6or7 = isIE6 || isIE7; var isChrome = navigator.userAgent.indexOf('Chrome') != -1; var $ = function (id) { return "string" == typeof id ? document.getElementById(id) : id; }; var Extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; } var CurrentStyle = function(element){ return element.currentStyle || document.defaultView.getComputedStyle(element, null); } var forEach = function(array, callback, thisObject){ if(array.forEach){ array.forEach(callback, thisObject); }else{ for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array); } } } var Filter = function(array, callback, thisObject){ if(array.filter){ return array.filter(callback, thisObject); }else{ var res = []; for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array) && res.push(array[i]); } return res; } } var Bind = function(object, fun) { var args = Array.prototype.slice.call(arguments).slice(2); return function() { return fun.apply(object, args.concat(Array.prototype.slice.call(arguments))); } } function addEventHandler(oTarget, sEventType, fnHandler) { if (oTarget.addEventListener) { oTarget.addEventListener(sEventType, fnHandler, false); } else if (oTarget.attachEvent) { oTarget.attachEvent("on" + sEventType, fnHandler); } else { oTarget["on" + sEventType] = fnHandler; } }; var TableFixed = function(table, options){ this._oTable = $(table);//原table this._nTable = this._oTable.cloneNode(false);//新table this._nTable.id = "";//避免id冲突 this._oTableLeft = this._oTableTop = this._oTableBottom = 0;//记录原table坐标参数 this._oRowTop = this._oRowBottom = 0;//记录原tr坐标参数 this._viewHeight = this._oTableHeight = this._nTableHeight = 0;//记录高度 this._nTableViewTop = 0;//记录新table视框top this._selects = [];//select集合,用于ie6覆盖select this._style = this._nTable.style;//用于简化代码 //chrome的scroll用document.body this._doc = isChrome ? document.body : document.documentElement; //chrome透明用rgba(0, 0, 0, 0) this._transparent = isChrome ? "rgba(0, 0, 0, 0)" : "transparent"; this.SetOptions(options); this._index = this.options.Index; this._pos = this.options.Pos; this.Auto = !!this.options.Auto; this.Hide = !!this.options.Hide; addEventHandler(window, "resize", Bind(this, this.SetPos)); addEventHandler(window, "scroll", Bind(this, this.Run)); this._oTable.parentNode.insertBefore(this._nTable, this._oTable); this.Clone(); }; TableFixed.prototype = { //设置默认属性 SetOptions: function(options) { this.options = {//默认值 Index: 0,//tr索引 Auto: true,//是否自动定位 Pos: 0,//自定义定位位置百分比(0到1) Hide: false//是否隐藏(不显示) }; Extend(this.options, options || {}); }, //克隆表格 Clone: function(index) { //设置table样式 this._style.width = this._oTable.offsetWidth + "px"; this._style.position = isIE6 ? "absolute" : "fixed"; this._style.zIndex = 100; //设置index this._index = Math.max(0, Math.min(this._oTable.rows.length - 1, isNaN(index) ? this._index : index)); //克隆新行 this._oRow = this._oTable.rows[this._index]; var oT = this._oRow, nT = oT.cloneNode(true); if(oT.parentNode != this._oTable){ nT = oT.parentNode.cloneNode(false).appendChild(nT).parentNode; } //插入新行 if(this._nTable.firstChild){ this._nTable.replaceChild(nT, this._nTable.firstChild); }else{ this._nTable.appendChild(nT); } //去掉table上面和下面的边框 if(this._oTable.border > 0){ switch (this._oTable.frame) { case "above" : case "below" : case "hsides" : this._nTable.frame = "void"; break; case "" : case "border" : case "box" : this._nTable.frame = "vsides"; break; } } this._style.borderTopWidth = this._style.borderBottomWidth = 0; //设置td样式 var nTds = this._nTable.rows[0].cells; forEach(this._oRow.cells, Bind(this, function(o, i){ var css = CurrentStyle(o), style = nTds[i].style; //设置td背景 style.backgroundColor = this.GetBgColor(o, css.backgroundColor); //设置td的width,没考虑ie8/chrome设scroll的情况 style.width = (document.defaultView ? parseFloat(css.width) : (o.clientWidth - parseInt(css.paddingLeft) - parseInt(css.paddingRight))) + "px"; })); //获取table高度 this._oTableHeight = this._oTable.offsetHeight; this._nTableHeight = this._nTable.offsetHeight; this.SetRect(); this.SetPos(); }, //获取背景色 GetBgColor: function(node, bgc) { //不要透明背景(没考虑图片背景) while (bgc == this._transparent && (node = node.parentNode) != document) { bgc = CurrentStyle(node).backgroundColor; } return bgc == this._transparent ? "#fff" : bgc; }, //设置坐标属性 SetRect: function() { //用getBoundingClientRect获取原table位置 var top = this._doc.scrollTop, rect = this._oTable.getBoundingClientRect(); this._oTableLeft = rect.left + this._doc.scrollLeft; this._oTableTop = rect.top + top; this._oTableBottom = rect.bottom + top; //获取原tr位置 rect = this._oRow.getBoundingClientRect(); this._oRowTop = rect.top + top; this._oRowBottom = rect.bottom + top; }, //设置新table位置属性 SetPos: function(pos) { //设置pos this._pos = Math.max(0, Math.min(1, isNaN(pos) ? this._pos : pos)); //获取位置 this._viewHeight = document.documentElement.clientHeight; this._nTableViewTop = (this._viewHeight - this._nTableHeight) * this._pos; this.Run(); }, //运行 Run: function() { if(!this.Hide){ var top = this._doc.scrollTop, left = this._doc.scrollLeft //原tr是否超过顶部和底部 ,outViewTop = this._oRowTop < top, outViewBottom = this._oRowBottom > top + this._viewHeight; //原tr超过视窗范围 if(outViewTop || outViewBottom){ var viewTop = !this.Auto ? this._nTableViewTop : (outViewTop ? 0 : (this._viewHeight - this._nTableHeight))//视窗top ,posTop = viewTop + top;//位置top //在原table范围内 if(posTop > this._oTableTop && posTop + this._nTableHeight < this._oTableBottom){ //定位 if(isIE6){ this._style.top = posTop + "px"; this._style.left = this._oTableLeft + "px"; setTimeout(Bind(this, this.SetSelect), 0);//iebug }else{ this._style.top = viewTop + "px"; this._style.left = this._oTableLeft - left + "px"; } return; } } } //隐藏 this._style.top = "-99999px"; isIE6 && this.ResetSelect(); }, //设置select集合 SetSelect: function() { this.ResetSelect(); var rect = this._nTable.getBoundingClientRect(); //把需要隐藏的放到_selects集合 this._selects = Filter(this._oTable.getElementsByTagName("select"), Bind(this, function(o){ var r = o.getBoundingClientRect(); if(r.top <= rect.bottom && r.bottom >= rect.top){ o._count ? o._count++ : (o._count = 1);//防止多个实例冲突 //设置隐藏 var visi = o.style.visibility; if(visi != "hidden"){ o._css = visi; o.style.visibility = "hidden"; } return true; } })) }, //恢复select样式 ResetSelect: function() { forEach(this._selects, function(o){ !--o._count && (o.style.visibility = o._css); }); this._selects = []; } }; </script> </head> <body style="height:1000px; width:1000px; padding-top:200px;"> <style type="text/css"> .tablefixed{width:600px; border-collapse:collapse;} .tablefixed td{ border:5px solid #999; padding:10px;} .tablefixed thead, .tablefixed tfoot{ background:#CCC;} </style> <table id="idTableFixed" class="tablefixed"> <thead> <tr> <td width="50"></td> <td>表头</td> <td width="100"></td> </tr> </thead> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/07/06/1236770.html">图片滑动切换效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/05/23/1205642.html">图片变换效果(ie only)</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/07/21/1247267.html">图片切割效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/09/15/1290954.html">仿LightBox内容显示效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/05/13/1194272.html">图片滑动展示效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/10/20/1314766.html">仿163网盘无刷新文件上传系统</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/11/17/1334778.html">拖放效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/10/05/1303993.html">图片切割系统</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/06/28/1231557.html">自定义多级联动浮动菜单</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/12/24/1360988.html">滑动条效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/12/03/1346386.html">拖拉缩放效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/08/27/1277131.html">渐变效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2008/10/06/1304414.html">Table排序</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2009/01/06/1369979.html">Tween算法及缓动效果</a></td> <td></td> </tr> <tr> <td></td> <td><a href="http://www.cnblogs.com/cloudgamer/archive/2009/03/11/1408333.html">颜色梯度和渐变效果</a></td> <td></td> </tr> <tfoot> <tr> <td></td> <td>表尾</td> <td></td> </tr> </tfoot> </table> <br /> 点击行选择克隆行:当前克隆第 <span id="idIndex">1</span> 行<br /> <br /> <input id="idPos" type="button" value="指定中间位置" /> <input id="idHide" type="button" value="取消定位" /> <br /> <br /> ps:为方便预览,建议缩小浏览器。 <script> var tf = new TableFixed("idTableFixed"); forEach($("idTableFixed").rows, function(o, i){ var n = i + 1; o.cells[0].innerHTML = n; o.cells[2].innerHTML = n % 4 ? " " : "<select><option>test</option></select>"; o.onclick = function(){ $("idIndex").innerHTML = n; tf.Auto = true; tf.Clone(i); } }); tf.Clone();//表格结构修改后应重新Clone一次 $("idPos").onclick = function(){ tf.Auto = false; tf.SetPos(.5); } $("idHide").onclick = function(){ if(tf.Hide){ tf.Hide = false; this.value = "取消定位"; }else{ tf.Hide = true; this.value = "显示定位"; } tf.Run(); } </script> </body> </html>
[Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]
注意,使用ie8的兼容性视图会有偏移。
程序原理
一开始的需求只是表头部分在滚动时能一直固定在头部,那关键要实现的就是让tr能定位。首先想到的方法是给tr设置relative,用ie6/7测试以下代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><body><table cellpadding="5" cellspacing="0" border="1" width="100"> <tr style="position:relative; left:100px;"> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> </tr></table></body></html>
给tr设置relative后就能相对table定位了,看来很简单啊,但问题是这个方法ie8和ff都无效,而且存在很多问题,所以很快就被抛弃了。ps:该效果用来做tr的拖动会很方便。
接着想到的是给table插入一个新tr,克隆原来的tr,并设置这个tr为fixed(ie6为absolute),例如:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><body><table cellpadding="5" cellspacing="0" border="1" width="100"> <tr style="position:fixed; left:100px;"> <td>1</td> <td>2</td> </tr> <tr style="position:absolute; left:200px;"> <td>3</td> <td>4</td> </tr> <tr> <td>5</td> <td>6</td> </tr></table></body></html>
第一个问题是fixed的tr在ie7中不能进行定位,而且td在定位后并不能保持在表格中的布局,这样在原表格插tr就没意义了。ps:fixed的相关应用可参考仿LightBox效果。
最后我用的方法是新建一个table,并把源tr克隆到新table中,然后通过对新table定位来实现效果。用这个方法关键有两点,首先要做一个仿真度尽可能高的tr,还有是要准确的定位,这些请看后面的程序说明。
http://www.blueidea.com/tech/web/2009/7031.asp
出处:蓝色理想 责任编辑:bluehearts
转载于:https://www.cnblogs.com/zhangxiaopeng/p/5138733.html
相关资源:数据结构—成绩单生成器