表格排序
先展开一下表格排序的思路:
- 获取表格的DOM引用来定位数据行 ————(获取到要排序的表格id,通过id获取到表格的tbody,html中要写成thead和tbody,把头部和数据区分开来,然后获取到行rows)
- 创建一个数组,将迭代tr元素并将它们放入数组中————(a.排序有sort()方法,此方法是对数组,但是rows是个DOM集合,并非数组,需要注意,解决方案就是创建一个数组。b.放入数组中不会从表格删除tr,其存储的是指针,不是实际元素)
- 对数组进行排序————(用sort(),但需要注意,sort()默认的是对字符的ASCII码顺序排列,只对准字符串,不适用数字、日期等....)
- 使用DOM将行按顺序逐个放置————(排列好之后发现页面顺序没变?改变顺序后要将每一行按序放回,这样数据多的时候就会影响性能,我们可以采用创建文档碎片,使用appendChild()给其传入一个文档碎片,最后添加的是碎片的所有子节点,并非碎片本身。下面附带为什么影响性能)
解释一些函数、词义及原因:
sort() 方法用于对数组的元素进行排序,
arrayObject.sort(sortby)
sortby可选,规定排序顺序,必须是函数。
调用该方法时没有使用参数,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。如果需要其他类型需要转换成对应的(一般用到得有整数、浮点数、日期,字符串)
如果想按照其他标准进行排序,就需要提供比较函数sortby,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
- 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
- 若 a 等于 b,则返回 0。
- 若 a 大于 b,则返回一个大于 0 的值。
reverse() 方法用于颠倒数组中元素的顺序。
js操纵DOM数据多的时候为什么会影响性能?解决办法是什么?
javascript操作dom是一个很耗性能的过程,在某些情况下,不得不进行dom循环操作,我们每次对dom的操作都会触发"重排",这严重影响到能耗,一般通常采取的做法是尽可能的减少dom操作来减少"重排"。
面对循环操作dom的过程,我们选择使用文档碎片(creatDocumentFragment),将需要添加到dom中的内容一次性添加到文档碎片中,然后将文档碎片添加到dom树,这样就可以有效的减少操作dom的次
什么是文档碎片?最后追加到页面的是否为文档碎片本身?
文档碎片:类似一个临时的文档,要所有要加的dom元素先放在这里,达到不要每次操作dom元素
创建方法:document.createDocumentFragment()
createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
当你想提取文档的一部分,改变,增加,或删除某些内容及插入到文档末尾可以使用createDocumentFragment() 方法。
你也可以使用文档的文档对象来执行这些变化,但要防止文件结构被破坏,createDocumentFragment() 方法可以更安全改变文档的结构及节点。
使用appendChild(),传给他一个文档碎片,最后添加的是碎片的所有子节点,并非碎片本身
传参的时候用到了闭包,什么是闭包?
较规范的解释是:所谓闭包,就是指此法表示包括不必计算的变量函数,也就是说,该函数能使用函数外定义的变量,在ECMAScript中使用全局变量就是一个简单的闭包。此代码中用到了一个较复杂的闭包,在一个函数中定义另外一个函数。
function generateCompareTRs(iCol,sDataType){ return function compareTRs(oTR1,oTR2){ var vValue1=convert(oTR1.cells[iCol].firstChild.nodeValue,sDataType); var vValue2=convert(oTR2.cells[iCol].firstChild.nodeValue,sDataType); //return sValue1.localeCompare(sValue2); if(vValue1<vValue2){ return -1; }else if(vValue1>vValue2){ return 1; }else{ return 0; } }; }
怎么给oTable添加其它的属性?用来保存排列的列索引
document.expando用于设置或获取表明是否可对象内创建任意变量的值
document.expando = false;此时,你在页面里面标签配置的默认属性之外的其他属性都将视为无效。//默认是true
代码中例:oTable.sortCol=iCol;
代码:
1 <table id="tableSort"> 2 <thead> 3 <tr> 4 <th width="30%" onclick="sortTable('tableSort' , 0, 'int')"><span class="sort_able">会员ID</span></th> 5 <th width="30%" onclick="sortTable('tableSort' , 1)"><span class="sort_able">会员名</span></th> 6 <th onclick="sortTable('tableSort' , 2 , 'date')"><span class="sort_able">注册时间</span></th> 7 </tr> 8 </thead> 9 <tbody> 10 <tr> 11 <td>16</td> 12 <td>webw3c</td> 13 <td>2011-04-13</td> 14 </tr> 15 <tr> 16 <td>45</td> 17 <td>test001</td> 18 <td>2011-03-27</td> 19 </tr> 20 <tr> 21 <td>116</td> 22 <td>wuliao</td> 23 <td>2011-04-01</td> 24 </tr> 25 <tr> 26 <td>29</td> 27 <td>tired</td> 28 <td>2011-04-06</td> 29 </tr> 30 <tr> 31 <td>155</td> 32 <td>tiredso</td> 33 <td>2011-04-06</td> 34 </tr> 35 <tr> 36 <td>31</td> 37 <td>javascript</td> 38 <td>2011-04-08</td> 39 </tr> 40 <tr> 41 <td>132</td> 42 <td>jquery</td> 43 <td>2011-04-12</td> 44 </tr> 45 </tbody> 46 </table>
//获取表格的DOM引用来定位数据行 function sortTable(sTableID , iCol,sDataType){ var oTable=document.getElementById(sTableID); var oTBody=oTable.tBodies[0]; var colDataRows=oTBody.rows; //创建一个数组,将tr元素放入其中,这样不会从表格中删除tr元素,存储的指针 var aTRs=new Array; for(var i=0;i<colDataRows.length;i++){ aTRs.push(colDataRows[i]); } //逆序时,判断索引是否与最后一次排序的列索引相同 if(oTable.sortCol==iCol){ aTRs.reverse(); }else{ aTRs.sort(generateCompareTRs(iCol,sDataType)); //对aTRs排序 } //对每一行按序放回,实现此操作最快的为创建文档碎片,将所有的tr元素按照正确顺序附加其上 //使用文档碎片在某些情况下可以提高页面效率。 var oFragment=document.createDocumentFragment(); for(var i=0;i<aTRs.length;i++){ oFragment.appendChild(aTRs[i]); } oTBody.appendChild(oFragment); oTable.sortCol=iCol; } //比较函数 function generateCompareTRs(iCol,sDataType){ return function compareTRs(oTR1,oTR2){ var vValue1=convert(oTR1.cells[iCol].firstChild.nodeValue,sDataType); var vValue2=convert(oTR2.cells[iCol].firstChild.nodeValue,sDataType); //return sValue1.localeCompare(sValue2); if(vValue1<vValue2){ return -1; }else if(vValue1>vValue2){ return 1; }else{ return 0; } }; } //针对多种类型 function convert(sValue,sDataType){ switch(sDataType){ case"int": return parseInt(sValue); case"float": return parseFloat(sValue); case"date": return new Date(Date.parse(sValue)); default: return sValue.toString(); } }
这样做需要在html代码中添加onclick,不太完美,稍后修改....
————————————————————————————————————————
之前的代码因为需要在html中添加点击事件,很不方便,就修改了一下,修改之后通过种种方法将代码升级,尽可能提高代码性能与减少代码的数量
这里贴上修改后js代码:
window.onload = function(){ var oTable=document.getElementById("tableSort"); oTable.addEventListener ("click" , fnclick , false); function fnclick(e){ var x = e.target; if(x.nodeName.toLowerCase() === 'th'){ sortTable("tableSort" , x.cellIndex) } } }; //获取表格的DOM引用来定位数据行 function sortTable(sTableID , iCol){ var oTable=document.getElementById(sTableID); var oTBody=oTable.tBodies[0]; var colDataRows=oTBody.rows; var aTRs=new Array; for(var i=0;i<colDataRows.length;i++){ aTRs.push(colDataRows[i]); } //排序 if(oTable.sortCol==iCol){ aTRs.reverse(); }else{ aTRs.sort(generateCompareTRs(iCol)); } var oFragment=document.createDocumentFragment(); for(var i=0;i<aTRs.length;i++){ oFragment.appendChild(aTRs[i]); } oTBody.appendChild(oFragment); oTable.sortCol=iCol; } //比较函数 function generateCompareTRs(iCol){ return function compareTRs(oTR1,oTR2){ var vValue1=oTR1.cells[iCol].firstChild.nodeValue; var vValue2=oTR2.cells[iCol].firstChild.nodeValue; if(!isNaN(vValue1) && !isNaN(vValue2)){ return parseInt(vValue1) - parseInt(vValue2); } return vValue1.localeCompare(vValue2); }; }
这个是修改第三遍的代码,第二遍的时候用到了获取th的class的值,因为javascript中并没有获取class值的方法,所以多添加了一个函数,这里贴出通过javascript获取class的函数
function getElementsByClassName(className, node, tag){ node = node || document;//如果省略了参数node,就从document中搜索,否则从node节点开始搜索 if(node.getElementsByClassName) return node.getElementsByClassName(className); else{ tag = tag || "*"; var searchElems = []; var elems = node.getElementsByTagName(tag); for(i=0; i<elems.length; i++){ var elem = elems[i]; if(elem.className.indexOf(className) != -1) searchElems.push(elem); } return searchElems; } }
第二遍时window.onload的函数是:
window.onload = function(){ var sortCols = getElementsByClassName("sort_able"); //sortCols.addEventListener ("click" , fnclick , false); for(i=0; i<sortCols.length; i++){ var sortCol = sortCols[i]; sortCol.onclick = function(){ sortTable("tableSort" , this.parentNode.cellIndex) }; } };
这里可以看出和第三遍的区别是,获取class,通过for循环遍历每一列的th,点击对应th时响应函数,为什么在第三遍换成了把绑定事件绑在了tableSort上,想起来昨天总结的事件机制(大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏)),因此通过冒泡来解决此问题,这样也不用获取class的值了,少了好大一段代码呢,嘿嘿....
不过html页面内容需要稍微修改一下,把span去掉包括class,因为获取事件目标,if(x.nodeName.toLowerCase() === 'th')不然这句匹配事件目标时,匹配到的就是span了
相比第一次的代码,还调整了一下转换函数,第一次的转换函数是比较精确,但是数据类型需要手动添加,我自己去判断没弄出来,很纠结....干脆就直接换掉了,代码也省去了好多,参数sDataType就不用了,他会直接判断,数字,时间,字符串问题也都解决了
突然想到另外的问题,
- 附加事件addEventListener(),它是DOM的附加事件的方法,IE与DOM不同,addEventListener()IE9以下不适用,
- IE与DOM获取事件的目标也不一样 IE:var oTarget=oEvent.srcElement; DOM: var oTarget=oEvent.target;
- 点击事件也不一样,IE在附加事件中点击事件需要写为“onclick”,DOM为“click”
除了这里用到的顺便贴出其它IE与DOM的不同
4、获取字符代码(随后添加例子吧,今天没精力了...)
5、阻止某个事件的默认行为
6、停止事件复制(冒泡)
还要进一步改代码......万恶的IE啊.....
终极代码
注意window.onload中代码的更改,代码可能还有待提高的地方吧,有些知识自己还没学到,还需要继续学js,随后发现再更改吧,四遍了,呜呜.....
window.onload = function(){ var oTable=document.getElementById("tableSort"); var clickType="click"; if(oTable.addEventListener){ oTable.addEventListener(clickType,fnclick,false); }else if(oTable.attachEvent){ oTable.attachEvent('on'+clickType,fnclick); }else{ oTable['on'+clickType]=fnclick; } //oTable.addEventListener ("click" , fnclick , false); function fnclick(e){ var x = e.target; if(x.nodeName.toLowerCase() === 'th'){ sortTable("tableSort" , x.cellIndex) } } }; //获取表格的DOM引用来定位数据行 function sortTable(sTableID , iCol){ var oTable=document.getElementById(sTableID); var oTBody=oTable.tBodies[0]; var colDataRows=oTBody.rows; var aTRs=new Array; for(var i=0;i<colDataRows.length;i++){ aTRs.push(colDataRows[i]); } //排序 if(oTable.sortCol==iCol){ aTRs.reverse(); }else{ aTRs.sort(generateCompareTRs(iCol)); } var oFragment=document.createDocumentFragment(); for(var i=0;i<aTRs.length;i++){ oFragment.appendChild(aTRs[i]); } oTBody.appendChild(oFragment); oTable.sortCol=iCol; } //比较函数 function generateCompareTRs(iCol){ return function compareTRs(oTR1,oTR2){ var vValue1=oTR1.cells[iCol].firstChild.nodeValue; var vValue2=oTR2.cells[iCol].firstChild.nodeValue; if(!isNaN(vValue1) && !isNaN(vValue2)){ return parseInt(vValue1) - parseInt(vValue2); } return vValue1.localeCompare(vValue2); }; }