近来有客户要求用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>
|