前言:打开一个页面,浏览器首先做的就是绘制节点树,也就是说,我们删除标签元素,添加标签元素,其实就是在删除节点,添加节点,也就是说在DOM节点数上进行操作。
一、节点树
(画工技术一般般,哈哈..儿童节快乐)
这是一个很简单的节点树,<!DOCTYPE html>相当于这棵节点说的根,也称为文档节点,文档节点有且只有一个。文档节点下也只有一个子节点,那就是<html>标签,我喜欢把它比作这棵节点树的主干,而<head>,<body>标签是<html>标签的子节点,就是主干上的两条分支。在<head>,<body>下又有更多的子节点,更多分支。
二、获取子节点(childNodes)
可以通过childNodes获取到该节点下的所有子节点类数组(NodeList类数组),NodeList类数组与我们以前见到的arguments类数组不同的是,Nodelist类数组有length属性。
<body> <ul id="myList"> hello ChildNodes <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); alert(element.childNodes.length); </script> <body>
猜一下输出结果吧。我相信没有接触过的朋友可能会说有3个li标签,那就是有3个子节点。或者是说4个节点,文字也算一个节点。
但是输出结果其实是7.
但是在iE8及IE8以下浏览器输出的是4
总结:其实在li标签之间还存在空白符,而空白符也被算是一个节点。我们试着不换行就行了。(文本节点前后不会产生空白符,因为空白符也是文本节点)
<body> <ul id="myList"> hello ChildNodes <li>Item 1</li><li>Item 2</li><li>Item 3</li></ul> <script> var element = document.getElementById('myList'); alert(element.childNodes.length); </script> <body>
这时候不存在空白符就输出了4了
三、子节点的名字(nodeName)
<body> <ul id="myList"> hello ChildNodes <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); for(var i =0,len=element.childNodes.length; i<len; i++){ console.log(element.childNodes[i].nodeName); } </script> <body>
输出结果
从输出结果终于可以看出那"神奇"的空白符节点的庐山真面目了,空白符节点也归类于文本节点。
四、子节点的节点类型(nodeType)
像上面例子中,我们接触到了三个节点类型:元素节点,文本节点。而我们比较长用到的就是元素节点跟文本节点了。像上面的例子,如果我们要剔除文本节点带来的困扰,我们可以通过判断他的节点类型。
元素节点的nodeType值为1;
文本节点的nodeType值为3;
<body> <ul id="myList"> hello ChildNodes <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); var length=0; for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ length++; } } console.log(length); </script> <body>
这时候就输出3了,有3个li标签,所以只有3个元素节点。
五、获取子节点的内容(nodeValue)
<div id="box"> 文本节点 <p>普通元素节点</p> <input type="text" value="表单元素节点"/> </div> <script> var node = document.getElementById('box'); console.log(node.childNodes[0].nodeValue); console.log(node.childNodes[1].innerText); console.log(node.childNodes[2].nodeValue); console.log(node.childNodes[3].value); </script> </body>
输出结果
这空白符节点的真是一片空白呀!
六、添加子节点
①、appendChild:在子节点的最后一位插入新节点
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; element.appendChild(newLi); //插入新创建的节点 </script> <body>
②insertBefore:在子节点的最后一位插入新节点
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; element.insertBefore(newLi,null); //插入新创建的节点 </script> <body>
③、insertBefore:在第一个子节点前面插入新节点
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; element.insertBefore(newLi,element.firstChild); //在第一个子节点前面插入新节点 </script> <body>
④、insertBefore:在任意元素子节点前面插入新节点
前面的例子已经展示过空白符文本节点带来的困扰,要排除空白符文本节点带来的困扰,我们需要进行其他处理下。
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var aNodeList = []; //用于保存元素节点对象 var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ aNodeList.push(element.childNodes[i]); } } element.insertBefore(newLi,aNodeList[1]); //在第任意元素子节点前面插入新节点 </script> <body>
这样我们只需要输入对应的元素子节点的索引,就可以往其前面插入子节点了。
⑤、insertBefore:多次插入同一个新节点时出现的问题
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var aNodeList = []; //用于保存元素节点对象 var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ aNodeList.push(element.childNodes[i]); } } element.insertBefore(newLi,aNodeList[0]); //在第一位元素子节点前面插入新节点 element.insertBefore(newLi,aNodeList[1]); //在第二位元素子节点前面插入新节点 element.insertBefore(newLi,aNodeList[2]); //在第二位元素子节点前面插入新节点 </script> <body>
运行代码:
发现并没有想象中的添加三个new Item,而只是添加一个而已。只是因为我们第一次添加新节点后,new Item已经是文档节点的一部分了,如果我们进行第二次添加,那结果就是将该新节点从原来的位置转移到新位置。任何DOM节点不能同时出现在文档中的多个位置上。
七、解决以上问题(克隆节点cloneNode)
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var aNodeList = []; //用于保存元素节点对象 var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; newLi.style.color = 'red'; for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ aNodeList.push(element.childNodes[i]); } } element.insertBefore(newLi,aNodeList[0]); //在第一位元素子节点前面插入新节点 var newLi1 = newLi.cloneNode(); //克隆newLi节点(没添加true) element.insertBefore(newLi1,aNodeList[1]); //在第二位元素子节点前面插入新节点 var newLi2 = newLi.cloneNode(true); //克隆newLi节点 element.insertBefore(newLi2,aNodeList[2]); //在第二位元素子节点前面插入新节点 </script> <body>
运行代码:
这时就很好的解决一个节点无法多次添加问题。cloneNode() 方法能克隆节点所有属性,(如果类名,属性),但是想要克隆节点的后代,如上面newLi里面的文字节点。
八、替换子节点(replaceChild)
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var aNodeList = []; //用于保存元素节点对象 var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ aNodeList.push(element.childNodes[i]); } } element.replaceChild(newLi,aNodeList[0]); //替换第一个元素子节点 element.replaceChild(newLi,aNodeList[1]); //替换第二个元素子节点 element.replaceChild(newLi,aNodeList[aNodeList.length-1]); //替换最后一个元素子节点 </script> <body>运行代码会发现,并不是出现三个"new Item",而是只有一个"new Item"。这是因为当替换一个节点时,该节点的所有关系指针都会被从被它替换的节点复制过来。就是说我们替换第二个元素子节点的时候,newLi已经是第一个元素子节点了,相当于我们先移除第二个元素子节点,然后把已经变成第一个元素子节点的newLi移到第二个元素子节点的位置上。
九、检测是否有改子节点(hasChildNodes)
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var element = document.getElementById('myList'); var newLi = document.createElement('li'); //创建新的节点 newLi.innerText = 'new Item'; element.appendChild(newLi); //插入新创建的节点 console.log(element.hasChildNodes(newLi)); //检测是否有newLi节点 </script> <body>
有就返回true,没有就返回false。
十、移除子节点
<body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> var aNodeList = []; //用于保存元素节点对象 var element = document.getElementById('myList'); for(var i =0,len=element.childNodes.length; i<len; i++){ if(element.childNodes[i].nodeType ==1 ){ aNodeList.push(element.childNodes[i]); } } element.removeChild(aNodeList[0]); //移除第一个元素子节点 element.removeChild(aNodeList[1]); //移除第二个元素子节点 element.removeChild(aNodeList[aNodeList.length-1]); //移除最后一个元素子节点 </script> <body>被移除的节点仍然未文档节点中(既是上面说的节点树的根中),只不过在文档节点中已经没有了它的位置了。