JavaScript Table排序 2.0 (更新)

近来还是那么忙,趁五一更新一下程序吧。
这个版本主要增加和改进了以下东西:
1,对字符串改用localeCompare来比较;
2,一次排序中能使用多个排序对象(用于值相等时再排序);
3,修正一些发现的问题;
4,改进程序结构,个人觉得是更灵活更方便了;
5,增加bool类型比较;
6,添加attribute/property的内容;
7,修正ie6/7的radio/checkbox状态恢复bug;
8,增加自定义取值函数。

Table 排序

.odTable {
width:500px;
border:1px solid #ebebeb;
line-height:20px;
font-size:12px;
background:#FFF;
}
.odTable thead td {
background-color:#ebebeb;
}
.odTable a {
outline:none;/*ff*/
hide-focus:expression(this.hideFocus=true);/*ie*/
}
.odTable a:link, .odTable a:visited, .odTable a:hover, .odTable a:active {
text-decoration:none;
color:#333;
padding-right:15px;
}
.odTable a.up, .odTable a.down {
background:url([img]/upload/20090507150209227.jpg[/img]) right center no-repeat;
}
.odTable a.down {
background-image:url([img]/upload/20090507150209265.jpg[/img]);
}

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 $ = 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 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)));
}
}

var forEach = function(array, callback, thisObject){
if(array.forEach){
array.forEach(callback, thisObject);
}else{
for (var i = 0, len = array.length; i

ID  名称 / 类型 上传时间 大小
1 new.htm 2008/9/12 423.09 K
2 Scroller.js 2008/9/23 2.5 K
3 AlertBox.js 2008/9/23 3.48 K
4 1.xml 2008/10/4 11.13 K
5 4.xml 2008/10/4 351 b
6 news.htm 2008/10/4 13.74 K
7 function.js 2008/10/4 2.78 K
8 神秘园 - Nocturne.mp3 2008/9/20 2.97 M
9 详细功略+剧情流程(一).doc 2009/2/2 62 K
10 详细功略+剧情流程(二).doc 2009/2/2 160.5 K
11 禁止文件预览功能.txt 2008/8/7 860 b
12 超级无敌精彩的效果集合.txt 2009/2/2 351 b

有中文的先排前面,再按时间倒序,ID倒序排序:

var to = new TableOrder("idTable"), odID = to.Creat({ DataType: "int", Desc: false }), arrOrder = [];

function ClearCss(){ forEach(arrOrder, function(o){ o.className = ""; }); }

function SetOrder(obj, options){
var o = $(obj), order = to.Creat(options);
order.startSort = function(){ ClearCss(); odID.Desc = this.Desc; }
order.endSort = function(){
o.className = this.Desc ? "down" : "up";//设置样式
this.Desc = !this.Desc;//取反排序
}
o.onclick = function(){ to.Sort(order, odID); return false; }
arrOrder.push(o);//记录排序项目(这里主要用来设置样式)
}

SetOrder("idNum", { DataType: "int" });
SetOrder("idTitle", { Index: 1 });
SetOrder("idExt", { Index: 1, Attri: "_ext" });
SetOrder("idAddtime", { Index: 2, DataType: "date" });
SetOrder("idSize", { Index: 3, Attri: "_order", DataType: "int" });

function GetCheck(o){
return o.getElementsByTagName("input")[0].checked;
}

SetOrder("idCheckbox", { Index: 4, DataType: "bool", GetValue: GetCheck });
SetOrder("idRadio", { Index: 5, DataType: "bool", GetValue: GetCheck });

$("idNum").onclick();

//////////////////////////////////////////////////////////////////////

var od1 = to.Creat({ Index: 1, endSort: ClearCss,
Compare: function(value1, value2) {
var re = /[\u4E00-\u9FA5]/i, v1 = re.test(value1), v2 = re.test(value2);
return v1 == v2 ? 0 : (v1 ? 1 : -1);
}
})
, od2 = to.Creat({ Index: 2, DataType: "date" })
, od3 = to.Creat({ DataType: "int" });

$("idBtn").onclick = function(){ to.Sort(od1, od2, od3); }

[Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

打包代码
基本步骤

1,把需要排序的行放到tbody中(程序会直接取tbody的rows);

2,把排序行放到一个数组中;

this.Rows = Map(this.tBody.rows, function(o){ return o; });

3,按需求对数组进行排序(用数组的sort方法);

this.Rows.sort(Bind(this, this.Compare, orders, 0));

4,用一个文档碎片(document.createDocumentFragment())保存排好序的行;

var oFragment = document.createDocumentFragment();
forEach(this.Rows, function(o){ oFragment.appendChild(o); });

ps:文档碎片并不是必须的,但建议使用,大量dom操作时使用文档碎片会更有效率。

5,把文档碎片插入到tbody中。

this.tBody.appendChild(oFragment);

程序说明

【排序函数】

排序就不得不说数组中sort这个方法,手册是这样介绍的:返回一个元素已经进行了排序的 Array 对象。也就是对一个数组进行排序,很多跟排序相关的操作都用到这个方法。

默认按照 ASCII 字符顺序进行升序排列,使用参数的话可以自定义排序方法,程序的Compare程序就是用来自定义排序的。
一般来说排序函数会有两个默认参数分别是两个比较对象,程序中根据需要在调用Compare时Bind了两个参数,所以会有4个参数。
要注意,排序函数必须返回下列值之一:
负值,如果所传递的第一个参数比第二个参数小。
零,如果两个参数相等。
正值,如果第一个参数比第二个参数大。

在取得比较值(后面说明)之后就进行值的比较。
程序中如果是字符串,会用localeCompare获取比较结果,否则就直接相减得到比较结果:

result = order.Compare ? order.Compare(value1, value2) ://使用自定义排序
typeof value2 == "string" ? value1.localeCompare(value2) : (value1 - value2);

如果Desc属性是true(倒序排序),那么在result的基础上乘以-1就能得到相反的排序了:(order.Down ? -1 : 1) * result

【获取比较值】

程序中是根据排序对象和GetValue方法从每个tr中获取比较值的。
首先通过Index(td索引)和Attri(属性)获取对应的值。
如果没有适合的属性放要比较的值的话,可以给td设一个自定义属性来放这个值(如例子中的_ext)。
对于在html中设置的自定义属性,ie可以用[x]和getAttribute来取,而ff就只能用getAttribute来获取(后面会详细说明)。
所以只需考虑ff的情况就行了,程序中用in来判断这个属性是否可以用[x]方式获取:

var td = tr.getElementsByTagName("td")[order.Index]
, at = order.Attri, data = at in td ? td[at] : td.getAttribute(at);

如果in运算是true,那么可以用关键词方式取值,否则用getAttribute来取。
取得值之后就进行比较值转换,这里会把日期用Date.parse转化成整数的形式方便比较:

switch (order.DataType.toLowerCase()) {
case "int":
return parseInt(data) || 0;
case "float":
return parseFloat(data) || 0;
case "date":
return Date.parse(data) || 0;
case "string":
default:
return data.toString() || "";
}

要注意Date.parse的参数必须符合js的日期格式(参考这里)。
ps:如果觉得添加自定义属性不符合标准,可以考虑放在title之类的属性中。

【attribute/property】

在获取比较值的时候会用in来判断是否可以用[x]方式,其实是判断该属性是属于attribute还是property。
那attribute和property到底是什么呢,有什么区别呢?这个或许很多人都没留意,或许认为是同一个东西。
要明确attribute和property是不同的东西就要先知道它们分别是什么,这个很难说得清,举些例子就明白了。
这里我们先以ff为标准,后面再说ie的区别。以div为例,查查网页制作完全手册,会找到它有以下属性:
ALIGN align
CLASS className
ID id
TITLE title
... ...
其中第一列就是attribute,第二列就是property。
attribute是dom元素在文档中作为html标签拥有的属性,property就是dom元素在js中作为对象拥有的属性。
例如在html中dom作为页面元素应该直接用class属性,对应在js中作为dom对象就必须用className属性。
由于attribute是不分大小写的,这使得大部分的attribute和property看起来会一样,使人误以为同一个东西(当然ie的责任也很大)。
还不相信的话可以用ff看看下面的例子:

<div id="t" tt="1">test </div>
<script>
var o = document.getElementById('t');
o["tt"]="2";
document.writeln(o.getAttribute("tt"));
document.writeln(o["tt"]);
</script>

可以看出getAttribute和[x]方式得到了不同的答案。
这里必须先说说getAttribute和[x]方式的区别,getAttribute和setAttribute是专门用来获取和设置attribute的,
而[x]方式就是获取和设置property属性的,这个property跟我们一般操作的js对象的属性是一样的。
或许有人会有疑问,像id,title不是都指向同一个属性吗,修改property对应attribute也会跟着修改。
其实我们也可以自定义一个这样的属性,在ff测试下面的代码:

<div id="t" tt="1">test </div>
<script>
var o = document.getElementById('t');
o.__defineSetter__("tt", function(x) { this.setAttribute("tt", x); });
o.__defineGetter__("tt", function() { return this.getAttribute("tt"); });
o.tt="2";
document.writeln(o.getAttribute("tt"));
document.writeln(o["tt"]);
</script>

这样就实现了“修改property对应attribute也会跟着修改”的属性了。
从测试例子还可以看到attribute跟对应的property完全可以使用不一样的属性名,像class和className的效果。
也能在Getter中对attribute的值进行处理再返回,就像href的property值是attribute的完整路径形式。
而property可以没有对应的attribute,反过来也一样,像innerHTML这样的property就没有对应的attribute。
ps:以上只是说明实现的原理,事实上并不需要这样来实现。

既然知道attribute和property是不同的东西,那如何分辨一个属性是属于attribute还是property呢。
我们可以用in来判断property,用hasAttribute判断attribute。
但ie6/7没有hasAttribute,是不是只能用in来判断呢?对了一半,ie6/7根本就不需要hasAttribute。
在ie6/7中,并没有很好地区分attribute和property。例如ie6/7运行下面代码:

<div id="t" tt="1">test </div>
<script>
var o = document.getElementById('t');
o["tt"]="2";
document.writeln(o.getAttribute("tt"));
document.writeln(o["tt"]);
o.setAttribute("tt","3");
document.writeln(o.getAttribute("tt"));
document.writeln(o["tt"]);
o["rr"]="4";
document.writeln(o.getAttribute("rr"));
document.writeln(o["rr"]);
document.writeln(o.getAttribute("innerHTML"));
document.writeln(o["innerHTML"]);
</script>

可以看到,里面基本没有attribute和property之分,而ie8的结果除了getAttribute("innerHTML"),其他跟ie6/7一样。
当然我觉得ie的制作者肯定知道attribute和property的区别,只是他们为了得到使用者想当然的结果,所以才这么弄。
本来被这么忽悠也没什么不好,但后来我发现一个问题:

<div id="t" class="a">test </div>
<script>
var o = document.getElementById('t');
o.setAttribute("class","b");
alert(o.outerHTML);
</script>

这样修改的样式是无效的,按照ie的规矩要使用className,但问题是从outerHTML中居然看到div标签中有两个class属性。
之前我一直都不知如何理解ie这个现象,不过这在ie8中已经得到了修正。
在ie8中已经把attribute和property区分开了(详细看Attribute Differences in Internet Explorer 8)。
例如getAttribute("innerHTML")返回的是null,说明innerHTML不再是attribute;setAttribute("class",x)修改的是attribute,不再是给dom元素添加一个莫名其妙的class属性,貌似getAttribute也没有了第二个参数(getAttribute的第二个参数可以看这里)。
不过ie8依然使用添加新属性会同时是attribute和property的模式,估计还是为了兼容之前的版本。

ps:以上都以[x]为例子,而使用.运算符的效果跟[x]是一样的。
ps2:由于对dom没有很深入的了解,这部分可能会有问题,欢迎各位指出。
ps3:发现自己的dom知识太少,正准备找本dom的书看看。

【radio/checkbox的checked状态bug】

可以用ie6/7测试下面代码:
<div id="c">
<input type="checkbox" id="a"
/>
<input name="b" type="radio" id="b"
/>
</div>
<input type="button" value="click" id="btn"
/>
<script>
var a = document.getElementById("a");
var b = document.getElementById("b");
var c = document.getElementById("c");
document.getElementById("btn").onclick =
function(){
c.appendChild(a);
c.appendChild(b);
var o1 = document.createElement("input");
o1.type =
"checkbox"; o1.checked =
true;
c.appendChild(o1);
var o2 = document.createElement("input");
o2.type =
"radio"; o2.checked =
true;
c.appendChild(o2);
}
</script>

先点选checkbox和radio,然后点击按钮,在ie6会发现checkbox和radio都恢复到没有点选的状态,ie7好点只是radio有问题。
而且新插入的checkbox和radio虽然checked都设置成true,但显示出来还是没有选择的状态。
这里其实都是一个问题,checkbox和radio在一些dom操作之后(例如这里的appendChild),checked会自动恢复成defaultChecked的状态。
创建元素的问题可以参考这里。
程序中tr在排序后会用appendChild重新插入文档,结果就会导致上面的问题了,解决方法暂时想到三个:
1,在appendChild之前修改defaultChecked。
针对appendChild后会自动恢复成defaultChecked,那我们就在appendChild前把defaultChecked修改成当前的checked值。
这个解决方法不错,只要appendChild之前扫一遍表单控件就行,但问题是这会影响到reset的结果,因为reset之后checkbox/radio的checked就会恢复成defaultChecked的值,如果修改了defaultChecked那reset就失去了效果。
2,在appendChild之前保存checked的状态,并在appendChild之后恢复。
要实现这个也有两个方法,一个是用一个数组或对象来保存checkbox/radio当前的checked值,在appendChild之后找出对应的值并设置。
另一个是直接把checkbox/radio当前的checked值保存到该控件的一个自定义属性中,在appendChild之后再获取并设置。
两个方法都要扫两次表单控件,后面的方法比较方便。
3,在appendChild之前找出checked跟defaultChecked不相等的控件,并在appendChild之后重新设置这些控件。
这个方法比前一个稍好,只要在appendChild之前扫一遍控件,并筛选出需要修正的(checked跟defaultChecked不相等的),在appendChild之后设置checked为defaultChecked的相反值就行了。
程序用的是第3个方法,在appendChild之前用GetChecked方法获取要修正的checkbox/radio集合:
GetChecked: function() {
this._checked = Filter(this.tBody.getElementsByTagName("input"), function(o){
return ((isIE6 && o.type ==
"checkbox") || o.type ==
"radio") && o.checked != o.defaultChecked;
});
},

在appendChild之后用SetChecked重新设置checked值:
SetChecked: function() {
forEach(this._checked, function(o){ o.checked =
!o.defaultChecked; });
}

但这样效率还是比较低,如果有更好的方法记得告诉我啊。

【排序对象】

为了程序的更灵活,加了一个排序对象的东西。
这个排序对象有以下属性:
属性 默认值//说明
Index: 0,//td索引
Attri: "innerHTML",//获取数据的属性
DataType: "string",//比较的数据类型
Desc: true,//是否按降序
Compare: null,//自定义排序函数
startSort: function(){},//排序前执行
endSort: function(){}//排序后执

可以看出这个排序对象就是用来保存该排序的规则和方式的,也就是用来告诉程序要怎么排序。
采用这个模式是因为一个table通常同时需要多个不同的排序方式,使用排序对象就像玩拳王选人,哪个适合就用哪个。
而程序在一次排序过程中还可以设置多个排序对象,一个被KO(比较值相等),另一个再上。
用这个方式会更方便,重用性更好。

程序中通过Creat程序来创建排序对象,其参数就是自定义的属性:

Creat: function(options) {
return Extend(Extend({}, this.options), options || {});
}

执行Sort程序就会进行排序,但必须一个或多个的排序对象为参数。
在Sort程序中会先把排序对象参数转换成数组:

var orders = Array.prototype.slice.call(arguments);

然后传递到Compare程序中,当比较结果是0(即相等),同时有下一个排序对象,就会用下一个排序对象继续Compare:

return !result && orders[++i] ? this.Compare(orders, i, o1, o2) : (order.Desc ? -1 : 1) * result;

这样的方式可以最大限度的利用已建立的排序对象。

使用方法

首先实例化一个主排序对象,参数是table的id:

var to = new TableOrder("idTable");

如果需要设置默认属性,一般建议在new的时候设置。

接着用Creat方法添加一个排序对象,参数是要设置的属性对象(参考【排序对象】):

odID = to.Creat({ DataType: "int", Desc: false })

然后就可以用Sort方法配合排序对象为参数来排序了:

to.Sort(order, odID);

(0)

相关推荐

  • javascript 实现简单的table排序及table操作练习

    在这个列子中,练习了table的操作,主要有:tBodies.rows.cells,还有有关数组的排序方法:sort 先上代码: 复制代码 代码如下: <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>table排序</title> </head> <body> <table id="tableTest&q

  • 一实用的实现table排序的Javascript类库

    一个Javascript 的类库,用于table内容排序.使用很方便,不用每次都去调用数据库了. 特别适合多表查询的排序.加上<tbody>的style.display 切换,也可以实现分页. 效果演示 用法: 1.添加JS <SCRIPT src="sorttable.js" type="text/javascript"></SCRIPT> 2.添加TABLE,注意的是:一定要有ID,class为"sortable&q

  • javascript小组件 原生table排序表格脚本(兼容ie firefox opera chrome)

    首先创建html页面为sort.html ,并把下面的内容复制进去 复制代码 代码如下: <!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"> &l

  • javascript实现Table排序的方法

    本文实例讲述了javascript实现Table排序的方法.分享给大家供大家参考.具体实现方法如下: <!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"&

  • javascript table排序 这个更简单了,不用改变现在的表格结构

    JavaScript实现可自定义排序的表格 - BODY { FONT-SIZE: 0.8em; FONT-FAMILY: Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif } P { FONT-WEIGHT: bold; MARGIN-BOTTOM: 0px } .tableWidget_headerCell { BORDER-RIGHT: #aca899 1px solid; BORDER-LEFT: #fff 1px solid; C

  • 实现超用户体验 table排序javascript实现代码

    以前我在网上总会看类似这种的JS效果: 点击编辑时: (不好意思哈,图在文章最后面) 这种列表与编辑都在同一页的效果,确实很好,能够给用户带来意想不到的美好体验.可是它是怎么实现的呢? 其实实现这种效果有很多方法,当然如果你的JS不好,可以用JQuery的组件,但是我想要是自己能写出来总是好的.因此我抽了点时间来实现它.当我实现后,发现要实现这种效果是很简单的,几行JS代码就搞定了,关键就是一个思路问题,下面我会具体的写是怎么实现的. 总体思路:给<TR>一个ID,以显示与隐藏来实现这种效果.

  • JavaScript Table排序 2.0 (更新)

    近来还是那么忙,趁五一更新一下程序吧. 这个版本主要增加和改进了以下东西: 1,对字符串改用localeCompare来比较: 2,一次排序中能使用多个排序对象(用于值相等时再排序): 3,修正一些发现的问题: 4,改进程序结构,个人觉得是更灵活更方便了: 5,增加bool类型比较: 6,添加attribute/property的内容: 7,修正ie6/7的radio/checkbox状态恢复bug: 8,增加自定义取值函数. Table 排序 .odTable { width:500px; b

  • jquery实现的table排序功能示例

    本文实例讲述了jquery实现的table排序功能.分享给大家供大家参考,具体如下: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus®"> <meta name="Author" co

  • js table排序类代码

    table排序类 .fu_list{ width:400px; border:1px solid #ebebeb;line-height:20px; font-size:12px;} .fu_list thead td{background-color:#ebebeb;} .fu_list td{padding:5px;} .fu_list a{outline:none;/*ff*/hide-focus:expression(this.hideFocus=true);/*ie*/ text-de

  • 用jquery.sortElements实现table排序

    项目中要实现table排序的功能. 网上有很多解决方案,很多都基于jQuery. jquery.tablesorter,大小17KB,不过他的首页在ie10下兼容性有点问题. DataTables,大小75KB,功能强大,带分页,搜索等功能. 还有插件叫sortElements,很小巧,只有3KB,兼容性也不错,而且在Github上有818个星. 最后我选择用sortElements,实现很简单: 1. 引入jQuery 复制代码 代码如下: <script type="text/java

  • javascript时间排序算法实现活动秒杀倒计时效果

    制做一个活动页面 秒杀列表页 需要一个时间的算法排序 自己琢磨了半天想了各种算法也没搞出来,后来问了下一个后台的php同学 他写了个算法给我看了下 ,刚开始看的时候觉得这就是个纯算法,不能转化成页面的dom效果,可是再看了两遍发现可以, 于是我就改了改,实现了,先分享给大家. 页面需求是:从11点到20点 每隔一个小时一场秒杀 如果是当前时间就显示正在秒杀 之前的商品就往最后排 以此类推 类似最开始的11点顺序是 11,12,13,14,15,16,17,18,19,20(点): 到12点的顺序

随机推荐