JavaScript Table行定位效果

时间:2023-12-21 23:46:56

作者:cloudgamer 时间: 2009-09-17 文档类型:原创 来自:蓝色理想

JavaScript Table行定位效果 第 1 页 JavaScript Table行定位效果 [1]
JavaScript Table行定位效果 第 2 页 JavaScript Table行定位效果 [2]
JavaScript Table行定位效果 第 3 页 JavaScript Table行定位效果 [3]
JavaScript Table行定位效果 第 4 页 JavaScript Table行定位效果 [4]
JavaScript Table行定位效果 第 5 页 JavaScript Table行定位效果 [5]
JavaScript Table行定位效果 第 6 页 JavaScript Table行定位效果 [6]
JavaScript Table行定位效果 第 7 页 JavaScript Table行定位效果 [7]
JavaScript Table行定位效果 第 8 页 JavaScript Table行定位效果 [8]

JavaScript Table行定位效果

近来有客户要求用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 ? "&nbsp;" : "<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