本帖最后由 c_hua6280 于 2010-02-24 14:53:47 编辑

解决方案 »

  1.   

    基本步骤 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的书看看。 【排序对象】为了程序的更灵活,加了一个排序对象的东西。
    这个排序对象有以下属性:
    属性  默认值//说明
    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);
     
      

  2.   

    程序源码TableOrder.prototype = {
      //设置默认属性
      SetOptions: function(options) {
        this.options = {//默认值
    Index: 0,//td索引
    Attri: "innerHTML",//获取数据的属性
    DataType: "string",//比较的数据类型
    Desc: true,//是否按降序
    Compare: null,//自定义排序函数
    startSort: function(){},//排序前执行
    endSort: function(){}//排序后执行
        };
        Extend(this.options, options || {});
      },
      //排序并显示
      Sort: function() {
    var orders = Array.prototype.slice.call(arguments);
    //没有排序对象返回
    if(!orders.length){ return false };
    //执行附加函数
    orders[0].startSort();
    //排序
    this.Rows.sort(Bind(this, this.Compare, orders, 0));
    //显示表格
    var oFragment = document.createDocumentFragment();
    forEach(this.Rows, function(o){ oFragment.appendChild(o); });
    this.tBody.appendChild(oFragment);
    //执行附加函数
    orders[0].endSort();
      },
      //比较函数
      Compare: function(orders, i, o1, o2) {
    var order = orders[i], value1 = this.GetValue(order, o1), value2 = this.GetValue(order, o2)
    ,result = order.Compare ? order.Compare(value1, value2) ://使用自定义排序
    typeof value2 == "string" ? value1.localeCompare(value2) : (value1 - value2);
    //如果result是0(值相同)同时有排序对象的话继续比较否则根据Desc修正结果并返回
    return !result && orders[++i] ? this.Compare(orders, i, o1, o2) : (order.Desc ? -1 : 1) * result;
      },
      //获取比较值
      GetValue: function(order, tr) {
    var td = tr.getElementsByTagName("td")[order.Index]
    , at = order.Attri, data = at in td ? td[at] : td.getAttribute(at);
    //数据转换
    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() || "";
    }
      },
      //创建并返回一个排序对象
      Creat: function(options) {
    return Extend(Extend({}, this.options), options || {});
      }
    }
      

  3.   

    有心要吗?
    用displaytag就可以对数据排序了,兄弟们不要自己累自己啊
      

  4.   

    好文章,受益匪浅,cloud很高产啊
      

  5.   

    如果只是显示文本内容这个功能还是很好做的。但有的时候表格里边可能会用input控件而有的行和列还
    绑定了函数这才是头疼的问题
      

  6.   

    没关系的啊
    你可以把要比较的值放到自定义属性
    里面的什么函数 input都没关系的
      

  7.   

    我曾经写过这样的函数所以知道那会出问题。我当时采用将需要排序的行科隆然后排序后重新appendChild
    输出,可是这样做依然不能解决函数绑定这些问题.不信你可以试验下如果你的一个行里边有checkbox,你选中
    以后再排序,你看看在ie6下还能不能保持住那个选中状态?那个问题还只是ie6的更普遍的是你的一个单元格
    用js绑定了一个处理函数比如:
    oCell.onclick=function(){
        //to-do:add you code here
    }
    你一移动的话这个函数就失效了,至少我试过用科隆的方法是不行的
      

  8.   

    也写了个这种排序器,也放到了csdn的资源里边,可是项目上用了几次发现问题越来越多。若要解决这些问题
    就要制定好多的限制,把一个简单的函数整的和控件似的,好多原来的表格就用不上,不解决的话功能又太
    单一只能用来处理纯文本的表格的现实,就向楼主给的那个demo中演示的那样。所以现在很矛盾不知道该怎么
    去做,我的同事反映这个问题很长时间了,跟他们解释后他们最后还是要么把那些input去掉要么放弃使用.
      

  9.   

    试了下不用科隆的话确实能解决函数绑定的问题,但是状态保存还是绕不过ie6的bug,看来我要重新考虑下
    如何修改这些bug更新我发布的资源了
    <!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=utf-8" />
    <title>无标题文档</title>
    </head>
    <body>
    <table id="tbl1" width="76%" border="1" cellspacing="0" cellpadding="0">
      <tr>
        <td align="center" bgcolor="#CCCCCC">列1</td>
        <td align="center" bgcolor="#CCCCCC">列2</td>
        <td align="center" bgcolor="#CCCCCC">列3</td>
        <td align="center" bgcolor="#CCCCCC">列4</td>
      </tr>
      <tr  id="tr2">
        <td>21</td>
        <td><input type="checkbox"/></td>
        <td><input type="text"/></td>
        <td><input type="radio" name="t2"/><input type="radio" name="t2"/></td>
      </tr>
      <tr id="tr1">
        <td id="td1">11</td>
        <td><input type="checkbox"/></td>
        <td><input type="text"/></td>
        <td><input type="radio" name="t1"/><input type="radio" name="t1"/></td>
      </tr>
    </table>
    <input type="button" value="移动" onclick="moveRow()"/>
    <script type="text/javascript">
    var $=function(sId){
    return document.getElementById(sId);
    }$("td1").onclick=function(){
    alert("your clicked a cell!");
    }function moveRow(){
    var oTB=$("tbl1").getElementsByTagName("tbody")[0];
    var oTr=oTB.removeChild($("tr1"));
    oTB.insertBefore(oTr,$("tr2"));
    }
    </script>
    </body>
    </html>
      

  10.   


    你可以用我那个套你的table试试啊
    应该没那些问题的
      

  11.   

    呵呵,我试下看看,我没仔细看粗略的溜了下,好像和我的思路差不多,都是用了ocumentFramenent。细节处理
    方面我的火候还不到处理的可能不如楼主好,不过通过今天的拍砖还是学到了不少。
      

  12.   

    看了下你的代码并没有处理这些ie6bug,我也下载了我的代码发现我后来也用了removeChild代替了cloneNode(true),只是时间长忘了。总的来说思路是一样,本身问题是不复杂,但你写的要比我的优雅些
    ,我的实现http://download.csdn.net/source/651985.勾选几个checkbox在ie6下排序下看看会有什么结果?
    改变那些已经默认勾选的选项排序下看看是否能保存出修改?
    因为代码太长了发不上来,所以只把我改动后的table发上来
    <table border="0" cellspacing="0" cellpadding="5" class="odTable" id="idTable">
    <thead>
    <tr>
    <td align="center"><a href="javascript:void(0)" id="idNum">ID</a></td>
    <td>&nbsp;<a href="javascript:void(0)" id="idTitle">名称</a> / <a href="javascript:void(0)" id="idExt">类型</a></td>
    <td width="150" align="center"><a href="javascript:void(0)" id="idAddtime">上传时间</a></td>
    <td width="50" align="center"><a href="javascript:void(0)" id="idSize">大小</a></td>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td align="center">1</td>
    <td _ext="htm"><input type="checkbox"/></td>
    <td align="center">2008/9/12</td>
    <td align="right" _order="433247">423.09 K</td>
    </tr>
    <tr>
    <td align="center">2</td>
    <td _ext="js"><input type="checkbox"/></td>
    <td align="center">2008/9/23</td>
    <td align="right" _order="2556">2.5 K</td>
    </tr>
    <tr>
    <td align="center">3</td>
    <td _ext="js"><input type="checkbox"/></td>
    <td align="center">2008/9/23</td>
    <td align="right" _order="3565">3.48 K</td>
    </tr>
    <tr>
    <td align="center">4</td>
    <td _ext="xml"><input type="checkbox"/></td>
    <td align="center">2008/10/4</td>
    <td align="right" _order="11394">11.13 K</td>
    </tr>
    <tr>
    <td align="center">5</td>
    <td _ext="xml"><input type="checkbox"/></td>
    <td align="center">2008/10/4</td>
    <td align="right" _order="351">351 b</td>
    </tr>
    <tr>
    <td align="center">6</td>
    <td _ext="htm"><input type="checkbox"/></td>
    <td align="center">2008/10/4</td>
    <td align="right" _order="14074">13.74 K</td>
    </tr>
    <tr>
    <td align="center">7</td>
    <td _ext="js"><input type="checkbox"/></td>
    <td align="center">2008/10/4</td>
    <td align="right" _order="2844">2.78 K</td>
    </tr>
    <tr>
    <td align="center">8</td>
    <td _ext="mp3"><input type="checkbox"/></td>
    <td align="center">2008/9/20</td>
    <td align="right" _order="3111293">2.97 M</td>
    </tr>
    <tr>
    <td align="center">9</td>
    <td _ext="doc"><input type="checkbox"/></td>
    <td align="center">2009/2/2</td>
    <td align="right" _order="63488">62 K</td>
    </tr>
    <tr>
    <td align="center">10</td>
    <td _ext="doc"><input type="checkbox"/></td>
    <td align="center">2009/2/2</td>
    <td align="right" _order="164352">160.5 K</td>
    </tr>
    <tr>
    <td align="center">11</td>
    <td _ext="txt">
                 <input type="checkbox" checked/>
                    <input type="radio" name="rad2"/>
                    <input type="radio" name="rad2"/>            
                </td>
    <td align="center">2008/8/7</td>
    <td align="right" _order="860">860 b</td>
    </tr>
    <tr>
    <td align="center">12</td>
    <td _ext="txt">
                 <input type="checkbox" checked="checked"/>
                    <input type="radio" name="rad1" checked/>
                    <input type="radio" name="rad1"/>
                </td>
    <td align="center">2009/2/2</td>
    <td align="right" _order="351">351 b</td>
    </tr>
    </tbody>
    </table>
      

  13.   

    目前我发现的能出bug的就checkbox和radio,其他的还没有试验,select也很可疑。这几个家伙都是ie6下
    的高危对象,其bug能把人整死。严重痛恨ie6
      

  14.   

    我目前能想到的只有这个办法,把radio和checkbox和其他有bug的控件不能带过去的状态在排序前扫描出来
    然后保存,排序后再赋值。
      

  15.   

    ie6是checkbox radio都有问题
    ie7是radio有问题
      

  16.   

      呵呵,向 Table 提问  ^_^