今天在帮人解决DOM节点问题时,把遇到的childNodes的空格问题在这总结下。
问题大概是这样的:把下面左边的四个节点一次性全部移到右边或把右边的节点一次性移到左边(>>右移,<<左移),
开始的实现是这样的:
<html> <head> <title>MyHtml.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> <script type="text/javascript"> function allToRigth(){ var nodes = document.getElementById("leftSelect").childNodes;//得到leftSelect的所有孩子节点 for(var i = 0; i < nodes.length; i ++){ //遍历所有孩子节点 if(nodes [i].nodeType == 1){ document.getElementById("rightSelect").appendChild(nodes[i]); //当孩子节点是元素节点时就右移 } } } function allToLeft(){ var nodes= document.getElementById("rightSelect").childNodes;//得到rightSelect的所有孩子节点 for(var i = 0; i < nodes.length; i ++){//遍历所有孩子节点 if(nodes[i].nodeType == 1){ document.getElementById("leftSelect").appendChild(nodes[i]); //当孩子节点是元素节点时就左移 } } } </script> </head> <body> <table width="200"> <td> <select size="4" id="leftSelect"> <option>hadoop</option> <option>hbase</option> <option>hive</option> <option>pig</option> </select> </td> <td> <input type="button" value=">>" id="allToRigth" onclick="allToRigth()"><br> <input type="button" value="<<" id="allToLeft" onclick="allToLeft()"> </td> <td> <select size="4" id="rightSelect"> </select> </td> </table> </body> </html>
结果:
再左移:(问题出现了:在再进行左移时并不能一次性把节点都移到左边)。
这里我我先分析下用 元素.childNodes 得到的节点的空格问题(测试所用浏览器及版本号:chrom 64位,IE11,火狐39.0,Edge);
现在的大部分浏览器在解析childNodes时都会把"#text"(空格,制表符,换行符等一些表示空白文本的符号)当成一个节点,但低版本的IE不会。
所以document.getElementById("leftSelect").childNodes得到的节点个数为9;
(左列表:两块黄色的部分表示一个节点,<option></option>表示一个节点,有9个节点)
下面是用FireBug调试的过程:
1.开始状态:
注意红色框部分,节点分别是TextNode,option,TextNode,option,TextNode,option,TextNode,option,TextNode.这9个节点(TextNode是空白文本)
2.第一个option节点移到右边后:
注意:在第一个option节点移走后,option后面的节点TextNode补了上来, 但在这次右移过程中这并不影响我们。
在全部节点移到右边后,我们来看左移这部分;
3.开始状态:有5个节点,一个TextNode节点,四个option节点。
4,第一个option节点移到左边后:
看吧,在第一个option节点移到左边后,i=2,后面的option节点补了上来,但补的位置又是已经访问过的位置(i=1)了,这样就造成了节点"遗漏"了,最后变成这样
这就是问题的根本原因了。
这里给出其中三种解决方案。
方案一:(仍旧用XML DOM)
思路:在取孩子节点时我们从最后一个孩子节点开始取,这样即使移走一个孩子节点后,后面的节点补上来也不影响前面孩子节点的位置。
代码实现:
<html> <head> <title>example2.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> <script type="text/javascript"> function allToRigth(){ var nodes = document.getElementById("leftSelect").childNodes;//得到leftSelect的所有孩子节点 for(var i = nodes .length-1; i >=0 ; i --){ //倒着遍历所有孩子节点 if(nodes [i].nodeType == 1){
//当孩子节点是元素节点时就插入到rightSelect的第一个孩子节点之前 document.getElementById("rightSelect").insertBefore(nodes [i],document.getElementById("rightSelect").firstChild); } } } function allToLeft(){ var nodes = document.getElementById("rightSelect").childNodes;//得到rightSelect的所有孩子节点 for(var i = nodes .length-1; i >=0 ; i --){ //倒着遍历所有孩子节点 if(nodes [i].nodeType == 1){
//当孩子节点是元素节点时就插入到leftSelect的第一个孩子节点之前 document.getElementById("leftSelect").insertBefore(nodes [i],document.getElementById("leftSelect").firstChild); } } } </script> </head> <body> <table width="200"> <td> <select size="4" id="leftSelect"> <option>hadoop</option> <option>hbase</option> <option>hive</option> <option>pig</option> </select> </td> <td> <input type="button" value=">>" id="allToRigth" onclick="allToRigth()"><br> <input type="button" value="<<" id="allToLeft" onclick="allToLeft()"> </td> <td> <select size="4" id="rightSelect"> </select> </td> </table> </body> </html>
方案二:(HTML DOM)
思路:上面我们提到在移走第一个节点后,后面的节点会补上来,最后造成节点"遗漏",那好,我们就始终只移下标为0的节点,这样就不会出现节点"遗漏"的情况了。
代码实现:
<html> <head> <title>MyHtml.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> <script type="text/javascript"> function allToRigth(){ var options = document.getElementById("leftSelect").options;//得到leftSelect中的所有options var oplength = options.length; for(var i = 0; i < oplength; i ++){ //遍历options,注意这里不能用i<options.length这种方式,因为在JS中的数组是可变的,在遍历过程中options.length //也是变化的,不然也会造成节点"遗漏" document.getElementById("rightSelect").add(options[0]); //始终只移第一个节点 } } function allToLeft(){ var options = document.getElementById("rightSelect").options;//得到rightSelect中的所有options var oplength = options.length; //得到options数组的长度 for(var i = 0; i < oplength; i ++){ document.getElementById("leftSelect").add(options[0]); //始终只移第一个节点 } } </script> </head> <body> <table width="200"> <td> <select size="4" id="leftSelect"> <option>hadoop</option> <option>hbase</option> <option>hive</option> <option>pig</option> </select> </td> <td> <input type="button" value=">>" id="allToRigth" onclick="allToRigth()"><br> <input type="button" value="<<" id="allToLeft" onclick="allToLeft()"> </td> <td> <select size="4" id="rightSelect"> </select> </td> </table> </body> </html>
方案二虽然比较便捷,但兼容性不如方案一。
方案三:
<html> <head> <title>MyHtml.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> <script type="text/javascript"> function allToRigth(){ var elementsLeft = document.getElementById("leftSelect"); var elementsRight = document.getElementById("rightSelect"); elementsRight.innerHTML = elementsLeft.innerHTML; elementsLeft.innerHTML = ''; } function allToLeft(){ var elementsLeft = document.getElementById("leftSelect"); var elementsRight = document.getElementById("rightSelect"); elementsLeft.innerHTML = elementsRight.innerHTML; elementsRight.innerHTML = ''; } </script> </head> <body> <table width="200"> <td> <select size="4" id="leftSelect"> <option>hadoop</option> <option>hbase</option> <option>hive</option> <option>pig</option> </select> </td> <td> <input type="button" value=">>" id="allToRigth" onclick="allToRigth()"><br> <input type="button" value="<<" id="allToLeft" onclick="allToLeft()"> </td> <td> <select size="4" id="rightSelect"> </select> </td> </table> </body> </html>
注意:在使用innerHTML时考虑浏览器的兼容性问题和安全性问题(特别是内存泄漏)