深入理解DOM节点操作
一般地,提起操作会想到“增删改查”这四个字,而DOM节点操作也类似地对应于此,接下来将详细介绍DOM的节点操作方法
前提
DOM提供节点操作的方法是因为DOM节点关系指针都是只读的
下列代码中想通过修改myUl的父级节点来修改其节点关系,但由于parentNode属性是只读的,所以修改无效,在IE8-浏览器下甚至会报错
<div id="myDiv"></div> <ul id="myUl"> <li id="myli"></li> </ul> <script> console.log(myUl.parentNode);//<body> myUl.parentNode = myDiv; //标准浏览器下,依然返回<body>;而IE8-浏览器则会报错 console.log(myUl.parentNode); </script>
DOM节点操作方法包括创建节点、插入节点、删除节点、替换节点、查看节点和复制节点。查看节点指的是查看节点之间的关系,在节点关系部分已经做过详细介绍,就不再赘述
创建节点
createElement()
document.createElement()方法可以创建新元素。这个方法接受一个参数,即要创建元素的标签名,这个标签名在HTML文档中不区分大小写
var oDiv = document.createElement("div"); console.log(oDiv);//<div>
IE8-浏览器可以为这个方法传入完整的元素标签,也可以包含属性
var oDiv = document.createElement('<div id="box"></div>'); console.log(oDiv.id);//'box'
利用这种方法可以避开IE7-浏览器在动态创建元素的下列问题
1、不能设置动态创建的<iframe>元素的name特性
2、不能通过表单的reset()方法重设动态创建的<input>元素
3、动态创建的type特性值为"reset"的<button>元素重设不了表单
4、动态创建的一批name相同的单选按钮彼此毫无关系。name值相同的一组单选按钮本来应该用于表示同一选项的不同值,但动态创建的一批这种单选按钮之间却没有这种关系
var iframe = document.createElement("<iframe name = 'myframe'></iframe>"); var input = document.createElement("<input type='checkbox'>); var button = document.createElement("<button type = 'reset'></button>"); var radio1 = document.createElement("<input type='radio' name ='choice' value = '1'>"); var radio2 = document.createElement("<input type='radio' name ='choice' value = '2'>");
所有节点都有一个ownerDocument的属性,指向表示整个文档的文档节点document;在使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性
<div id="myDiv"></div> <script> console.log(myDiv.ownerDocument);//document var newDiv = document.createElement('div'); console.log(newDiv.ownerDocument);//document console.log(newDiv.ownerDocument === myDiv.ownerDocument);//true </script>
插入节点
appendChild()
appendChild()方法用于向childNodes列表的末尾添加一个节点,并返回新增节点。添加节点后,childNodes中的新增节点、父节点和以前的最后一个子节点的关系指针都会相应地得到更新
<div id="box"></div> <script> var oBox = document.getElementById('box'); var newNode = document.createElement('ul'); var returnedNode = oBox.appendChild(newNode); console.log(returnedNode.nodeName);//UL console.log(returnedNode == newNode);//true console.log(returnedNode == oBox.lastChild);//true </script>
如果插入的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置
<body> <div id="oldDiv">第一个div</div> <div id="newDiv">第二个div</div> <button id="btn">变换位置</button> <script> btn.onclick = function(){ document.body.appendChild(newDiv); } </script> </body>
insertBefore()
insertBefore()方法接收两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个兄弟节点(previousSibling),同时被方法返回。如果参照节点是null,则insertBefore()与appendChild()方法执行相同的操作。同样地,如果插入的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置
referenceNode.parentNode.insertBefore(newNode,referenceNode);
<ul id="myUl" style="border:1px solid black;"> <li id="myLi"> <div id='oldDiv'>oldDiv</div> </li> </ul> <button id="btn1">插入oldDiv的前面</button> <button id="btn2">插入myUl的前面</button> <button id="btn3">插到oldDiv的里面</button> <script> var oDiv = document.createElement('div'); oDiv.innerHTML = 'newDiv'; btn1.onclick = function(){ console.log(myLi.insertBefore(oDiv,oldDiv));//<div>newDiv</div> } btn2.onclick = function(){ console.log(document.body.insertBefore(oDiv,myUl));//<div>newDiv</div> } btn3.onclick = function(){ console.log(oldDiv.insertBefore(oDiv,null));//<div>newDiv</div> } </script>
【小效果】
<ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in">4</li> <li class="in">5</li> <li class="in">6</li> </ul> <script> var oList = document.getElementById('list'); //新增一个li元素 var oAdd = document.createElement('li'); //设置新增元素的css样式 oAdd.className = "in"; oAdd.style.cssText = 'background-color:red;border-radius:50%'; //添加到oList中 oList.insertBefore(oAdd,null); var num = -1; var max = oList.children.length; function incrementNumber(){ num++; //oList.getElementsByTagName('li')[max]相当于null,所以不报错 oList.insertBefore(oAdd,oList.getElementsByTagName('li')[num]); if(num == max){ num = -1; } if(num == 0){ num = 1; } setTimeout(incrementNumber,1000); } setTimeout(incrementNumber,1000); </script>
insertAfter()
由于不存在insertAfter()方法,如果要插在当前节点的某个子节点后面,可以用insertBefore()和appendChild()封装方法
function insertAfter(newElement,targetElement){ var parent = targetElement.parentNode; if(parent.lastChild == targetElement){ parent.appendChild(newElement); }else{ parent.insertBefore(newElement,targetElement.nextSibling) } }
<div id='oldDiv'>old</div> <button id="btn">增加节点</button> <script> function insertAfter(newElement,targetElement){ var parent = targetElement.parentNode; if(parent.lastChild == targetElement){ return parent.appendChild(newElement); }else{ return parent.insertBefore(newElement,targetElement.nextSibling) } } var newDiv = document.createElement('div'); newDiv.innerHTML = 'new'; btn.onclick = function(){ insertAfter(newDiv,oldDiv); } </script>
insertAdjacentHTML()
insertAdjacentHTML()方法作为终级办法,相当于前面三个方法的综合。该方法接收两个参数:插入的位置和要插入的HTML文本
第一个参数必须是下列值之一,且这些值都必须是小写形式:
"beforebegin" 在当前元素之前插入一个紧邻的同级元素 "afterbegin" 在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素 "beforeend" 在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素 "afterend" 在当前元素之后插入一个紧邻的同级元素
第二个参数是一个HTML字符串,如果浏览器无法解析字符串,就会抛出错误
[注意]该方法无返回值
<div id='target' style="border: 1px solid black;">This is the element content</div> <button>beforebegin</button> <button>afterbegin</button> <button>beforeend</button> <button>afterend</button> <script> var btns = document.getElementsByTagName('button'); for(var i = 0 ; i < 4; i++){ btns[i].onclick = function(){ var that = this; target.insertAdjacentHTML(that.innerHTML,'<span id="test">测试</span>') } } </script>
移除节点
removeChild()
removeChild()方法接收一个参数,即要移除的节点,被移除的节点成为方法的返回值
<div id="myDiv">等待移除的节点</div> <button id="btn">移除节点</button> <script> btn.onclick = function(){ document.body.removeChild(myDiv); } </script>
下面代码可以移除当前节点的所有子节点
var element = document.getElementById("top"); while (element.firstChild) { element.removeChild(element.firstChild); }
【小效果】
<button id="btn">开始删除节点</button> <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in">4</li> <li class="in">5</li> <li class="in">6</li> </ul> <script> var oList = document.getElementById('list'); function incrementNumber(){ //获取oList中子元素的个数 var len = oList.getElementsByTagName('li').length; //如果长度不为0 if(len){ //删除最后一个子元素 oList.removeChild(oList.getElementsByTagName('li')[len-1]); //再次调用计时器 setTimeout(incrementNumber,1000); } } btn.onclick = function(){ //1s后执行函数incrementNumber setTimeout(incrementNumber,1000); } </script>
remove()
相比于removeChild(),remove()方法不太常见,但是却非常简单。该方法不用调用其父节点,直接在当前节点使用remove()方法就可以删除该节点,无返回值
remove()方法常用于删除元素节点和文本节点,不可用于特性节点
[注意]IE浏览器不支持该方法
<div id="test" title='div'>123</div> <script> //文本节点 console.log(test.childNodes[0]);//'123' test.childNodes[0].remove(); console.log(test.childNodes[0]);//undefined //特性节点 console.log(test.attributes.title);//'div' //报错,remove()方法无法用于删除特性节点 try{test.attributes[0].remove()}catch(e){ console.log('error'); } //元素节点 console.log(test); test.remove(); </script>
替换节点
replaceChild()
replaceChild()接收的两个参数是要插入的节点和要替换的节点,要替换的节点将由这个方法返回并从文档树中移除,同时由要插入的节点占据其位置
oldChild.parentNode.replaceChild(newChild, oldChild);
<div id="div1">1</div> <div id="div2">2</div> <div id="div3">3</div> <button id="btn1">新增节点替换(4替换2)</button> <button id="btn2">原有节点替换(3替换1)</button> <script> btn2.onclick = function(){ document.body.replaceChild(div3,div1); } btn1.onclick = function(){ var div4 = document.createElement('div'); div4.innerHTML = '4'; document.body.replaceChild(div4,div2); } </script>
【小效果】
<button id="btn">开始替换节点</button> <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in">4</li> <li class="in">5</li> <li class="in">6</li> </ul> <script> var oList = document.getElementById('list'); //新增一个li元素 var oAdd = document.createElement('li'); //设置新增元素的css样式 oAdd.className = "in"; oAdd.style.cssText = 'background-color:red;border-radius:50%'; btn.onclick = function(){ //1s后oAdd替换第0个li setTimeout(function(){ oList.replaceChild(oAdd,document.getElementsByTagName('li')[0]); //1s后执行incrementNumber函数 setTimeout(incrementNumber,1000); },1000); } function incrementNumber(){ //获取oList中第1个li var oLi1 = document.getElementsByTagName('li')[1]; //若存在则进行替换处理 if(oLi1){ oList.replaceChild(oAdd,oLi1); setTimeout(incrementNumber,1000); } } </script>
复制节点
cloneNode()
cloneNode方法用于克隆一个节点。它接受一个布尔值作为参数,表示是否执行深复制。在参数为true时,执行深复制,也就是复制节点及整个子节点树。在参数为false的情况下,执行浅复制,即复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。若参数为空,也相当于false
[注意]cloneNode()方法不会复制添加到DOM节点中的javascript属性,例如事件处理程序等。这个方法只复制特性和子节点,其他一切都不会复制
<ul id="list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul> <script> var oList = document.getElementById('list'); oList.index = 0; var deepList = oList.cloneNode(true); //成功复制了子节点 console.log(deepList.children.length);//6 //但并没有复制属性 console.log(deepList.index);//undefined var shallowList = oList.cloneNode(); //浅复制不复制子节点 console.log(shallowList.children.length);//0 </script>