JavaScript DOM文檔遍歷實(shí)戰(zhàn)
在介紹了《JavaScript DOM修改文檔樹方法實(shí)例》與《JavaScript DOM實(shí)戰(zhàn):創(chuàng)建和克隆元素》,本文將介紹JavaScript DOM文檔遍歷的詳細(xì)知識(shí),我們先從HTML頁(yè)面最基本的組成元素討論起:
- <!--<html>
- <head>
- <title>DOM Examlie</title>
- </head>
- <body>
- <p>Hello World !</p>
- </body>
- </html>-->
訪問<html>元素,你應(yīng)該明白它是該文件的document元素,那你就可以使用document的documentElement屬性:
- var oHtml=document.documentElement;//可以直接訪問<html>元素
- alert("節(jié)點(diǎn)名稱 : "+oHtml.nodeName);//節(jié)點(diǎn)名稱
- alert("節(jié)點(diǎn)類型 : "+oHtml.nodeType);//節(jié)點(diǎn)類型為 1
獲取<head> 和 <body>元素:
- var oHead=oHtml.firstChild;//HEAD節(jié)點(diǎn)
- var oBody=oHtml.lastChild;//BODY節(jié)點(diǎn)
也可以通過childNodes屬性,獲取<head> 和 <body>元素:
- var oHead=oHtml.childNodes.item(0);//HEAD節(jié)點(diǎn)
- //var oHead=oHtml.childNodes[0];//簡(jiǎn)寫,也有同樣的結(jié)果是HEAD節(jié)點(diǎn)
- var oBody=oHtml.childNodes.item(1);//BODY節(jié)點(diǎn)
- //var oBody=oHtml.childNodes.item(1);//簡(jiǎn)寫,也有同樣的結(jié)果是BODY節(jié)點(diǎn)
注意:方括號(hào)標(biāo)記其實(shí)是NodeList在javascript中的簡(jiǎn)便實(shí)現(xiàn)。實(shí)際上正式的從childNodes列表中獲取子節(jié)點(diǎn)的方法是使用item()方法:HTML DOM 中的專有屬性 document.body ,它常用于直接訪問元素:
- var oBody=document.body;
既然我們都知道了以上節(jié)點(diǎn)對(duì)象的獲取方式,那我們用oHtml,oHead,oBody 這三個(gè)變量來確定一下它們之間的關(guān)系:
- alert(oHead.parentNode==oHtml);//HEAD節(jié)點(diǎn)的父節(jié)點(diǎn)是BODY節(jié)點(diǎn),返回 true
- alert(oBody.parentNode==oHtml);//BODY節(jié)點(diǎn)的父節(jié)點(diǎn)是BODY節(jié)點(diǎn),返回 true
- alert(oBody.previousSibling==oHead);//BODY節(jié)點(diǎn)的上一個(gè)兄弟節(jié)點(diǎn)是HEAD節(jié)點(diǎn) ,返回 true
- alert(oHead.nextSibling==oBody);//HEAD節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn)是BODY節(jié)點(diǎn),返回 true
- alert(oHead.ownerDocument==document); //返回一個(gè)節(jié)點(diǎn)的根元素(Document),HEAD節(jié)點(diǎn)是否指向該文檔,返回 true
通過上面的學(xué)習(xí)我們已經(jīng)了解遍歷節(jié)點(diǎn)的最基本的方式, 也學(xué)會(huì)了如何找到某一個(gè)節(jié)點(diǎn)的兄弟節(jié)點(diǎn)及它的子節(jié)點(diǎn)。
#p#
復(fù)雜的節(jié)點(diǎn)遍歷
在上面的學(xué)習(xí)中我們好像沒有遇到過大的阻礙,下面我們以一個(gè)“導(dǎo)航條”為實(shí)例:
- <div id="menu">
- <h1>我的導(dǎo)航條</h1>
- <ul id="nav">
- <li><a href="#">HOME</a></li>
- <li><a href="#">(X)Html / Css</a></li>
- <li><a href="#">Ajax / RIA</a></li>
- <li><a href="#">GoF</a></li>
- <li><a href="#">JavaScript</a></li>
- <li><a href="#">JavaWeb</a></li>
- <li><a href="#">jQuery</a></li>
- <li><a href="#">MooTools</a></li>
- <li><a href="#">Python</a></li>
- <li><a href="#">Resources</a></li>
- </ul>
- </div>
首先我想把看一下我的導(dǎo)航條下有多少個(gè)子節(jié)點(diǎn)。我***想到的是前面我學(xué)過的查找元素的2種方法:
◆getElementById() # 通過ID屬性查找元素
該方法將返回一個(gè)與那個(gè)有著給定id屬性值的元素節(jié)點(diǎn)相對(duì)應(yīng)的對(duì)象。
◆getElementsByTagName() # 通過標(biāo)簽名稱查找元素
該方法返回一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象分別對(duì)應(yīng)著文檔里有著給定標(biāo)簽的一個(gè)元素。
- <script type="text/javascript">
- /*
- 通過ID屬性查找元素 ,用的是文檔對(duì)象的getElementById()方法,
- 查找到我們想要的元素對(duì)象,根據(jù)這個(gè)節(jié)點(diǎn)元素的 childNodes 屬性,
- 遍歷出所有的子節(jié)點(diǎn)對(duì)象。
- */
- function queryElementsId(){
- var elemensArray,nav,nav_list;
- elemensArray=[];
- nav=document.getElementById("nav");
- /*注意IE和FF中處理Text節(jié)點(diǎn)上存在著一些差異*/
- navnav_list=nav.childNodes;
- for(var i=0;i<nav_list.length;i++){
- elemensArray[elemensArray.length]=nav_list[i];
- //elemensArray.push(nav_list[i]); //同上一樣的結(jié)果
- }
- return elemensArray;
- }
- /*
- 我們觀察到我的導(dǎo)航條是有規(guī)律的,是用無序列表元素組成的,只有定位到 <ul>元素
- ;然后把getElementsByTagName()方法可以返回相同元素對(duì)象的集合,
- 查用它找一組元素,太方便了。
- */
- function queryElementsTagName(){
- var elemensArray,nav,nav_list;
- elemensArray=[];
- var nav=document.getElementById("nav");
- var navnav_list=nav.getElementsByTagName("li");//返回相同的一組元素
- for(var i=0;i<nav_list.length;i++){
- elemensArray[elemensArray.length]=nav_list[i];
- //elemensArray.push(nav_list[i]); //同上一樣的結(jié)果
- }
- return elemensArray;
- }
- </script>
節(jié)點(diǎn)遍歷
那我們接下來,測(cè)一下是否是我們想要的東西:
- <script type="text/javascript">
- window.onload=function(){
- /****個(gè)方法*/
- var list= queryElementsId();
- /*第二個(gè)方法*/
- //var list= queryElementsTagName();
- var s="";
- for(var i=0;i<list.length;i++){
- s+=list[i].nodeName+"\n";
- }
- alert(s);
- }
- </script>
先看一下***個(gè)方法queryElementsId()好像我們?cè)贗E中沒有發(fā)現(xiàn)有什么問題,那我們?cè)贔irefox中看一下是否也是我們想要的結(jié)果。
這時(shí),問題出現(xiàn)了,不同的瀏覽器在判斷何為Text節(jié)點(diǎn)上存在著一些差異,例如在A級(jí)瀏覽器中的FF和IE就有很大的差異,F(xiàn)ireFox會(huì)把元素之間的空白、換行、tab都是Text節(jié)點(diǎn),IE下會(huì)把空白全部忽略掉,只有內(nèi)聯(lián)元素(如em,span)后的換行、空格、tab會(huì)被認(rèn)為是一個(gè)Text。既然遇到了問題那我們就得解決問題,問題的根源我們也知道了,那相應(yīng)的解決方案就好做了。
方法一:
- <script type="text/javascript">
- /*
- 《精通javascript》上提供了一個(gè)函數(shù),用于處理xm中的這些空格,其作用原理就是找出文本節(jié)點(diǎn),并刪除這些節(jié)點(diǎn),以達(dá)到刪除這些空格的目的。
- */
- function cleanWhitespace(element){
- //如果不提供參數(shù),則處理整個(gè)HTML文檔
- elementelement = element || document;
- //使用***個(gè)子節(jié)點(diǎn)作為開始指針
- var cur = element.firstChild;
- //一直到?jīng)]有子節(jié)點(diǎn)為止
- while (cur != null){
- //如果節(jié)點(diǎn)為文本節(jié)點(diǎn),應(yīng)且包含空格
- if ( cur.nodeType == && ! /\S/.test(cur.nodeValue)){
- //刪除這個(gè)文本節(jié)點(diǎn)
- element.removeChild( cur );
- //否則,它就是一個(gè)元素
- } else if (cur.nodeType == 1){
- //遞歸整個(gè)文檔
- cleanWhitespace( cur );
- }
- curcur = cur.nextSibling;//遍歷子節(jié)點(diǎn)
- }
- }
- </script>
方法二:
- <script type="text/javascript">
- /*
- ***,利用數(shù)組寫了一個(gè)函數(shù),能夠有效的處理dom中的空格,其原理就是將一個(gè)元素的的父元素找出來,然后通過它父元素的childNodes屬性找出該元素的所有兄弟元素。遍歷該元素和它的兄弟元素,將所有元素節(jié)點(diǎn)放在一個(gè)數(shù)組里。這樣調(diào)用這個(gè)數(shù)組,就只有元素節(jié)點(diǎn)而沒有文本節(jié)點(diǎn),也就沒有了討厭的空格.
- */
- function cleanWhitespaces(elem){
- //如果不提供參數(shù),則處理整個(gè)HTML文檔
- var elemelem = elem || document;
- var parentElem = elem.parentNode; //返回一個(gè)節(jié)點(diǎn)的父類節(jié)點(diǎn)
- var childElem = parentElem.childNodes; //返回一個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)的節(jié)點(diǎn)列表
- var childElemArray = new Array;
- for (var i=0; i<childElem.length; i++){
- if (childElem[i].nodeType==1){//把所有節(jié)點(diǎn)是元素節(jié)點(diǎn)類型的節(jié)點(diǎn)存放到數(shù)組里
- childElemArray.push(childElem[i]);
- }
- }
- return childElemArray;
- }
- </script>
方法三:推薦
- <script type="text/javascript">
- /*
- 原理是對(duì)元素的所有的子節(jié)點(diǎn)做一個(gè)遍歷。然后做一個(gè)判斷,如果是子元素節(jié)點(diǎn)(nodeType = 1),則遍歷該子元素的所有的子節(jié)點(diǎn),用遞歸檢查是否包含空白節(jié)點(diǎn);如果處理的子節(jié)點(diǎn)是文本節(jié)點(diǎn)(nodeType = 3),則檢查是否是純粹的空白節(jié)點(diǎn),如果是,就將它從xml對(duì)象中刪除。
- */
- function removeWhitespace(xml){
- var loopIndex;
- for (loopIndex = 0; loopIndex < xml.childNodes.length; loopIndex++){
- var currentNode = xml.childNodes[loopIndex];
- if (currentNode.nodeType == 1){
- removeWhitespace(currentNode);
- }
- if (((/^\s+$/.test(currentNode.nodeValue))) &&(currentNode.nodeType == 3)){
- xml.removeChild(xml.childNodes[loopIndex--]);
- }
- }
- }
- </script>
好了,我們?cè)隍?yàn)證一下,#Text節(jié)點(diǎn)問題是否處理掉了。那我們就用方法3 中removeWhitespace(nav)方法來處理queryElementsId()方法中的#Text節(jié)點(diǎn)問題。
- <script type="text/javascript">
- function queryElementsId(){
- var elemensArray,nav,nav_list;
- elemensArray=[];
- nav=document.getElementById("nav");
- /*處理#Text節(jié)點(diǎn)問題*/
- removeWhitespace(nav);
- /*注意IE和FF中處理Text節(jié)點(diǎn)上存在著一些差異*/
- navnav_list=nav.childNodes;
- for(var i=0;i<nav_list.length;i++){
- elemensArray[elemensArray.length]=nav_list[i];
- //elemensArray.push(nav_list[i]); //同上一樣的結(jié)果
- }
- return elemensArray;
- }
- </script>
正如看到的結(jié)果,IE和FireFox中都沒有問題了。
#p#
一個(gè)比較通用的方法:
- <script type="text/javascript">
- function text(elem){
- var t="";
- //如果傳入的是元素,則繼續(xù)遍歷其子元素
- //否則假定它是一個(gè)數(shù)組
- elemelem=elem.childNodes || elem;
- //遍歷所有子節(jié)點(diǎn)
- for(var i=0; i<elem.length;i++){
- //如果不是元素,追加其文本值
- //否則,遞歸遍歷所有元素的子節(jié)點(diǎn)
- t+=elem[i].nodeType !=1?elem[i].nodeValue:text(elem[i].childNodes);
- }
- //返回比配的文本
- return t;
- }
- </script>
用元素節(jié)點(diǎn)的DOM屬性遍歷DOM樹
- <script type="text/javascript">
- window.onload=function(){
- /*定位想要的節(jié)點(diǎn)*/
- var nav=document.getElementById("nav");
- /*查找父節(jié)點(diǎn)*/
- var p_n=nav.parentNode;
- alert("父節(jié)點(diǎn)的元素名稱:"+p_n.nodeName);
- /*處理FF遍歷節(jié)點(diǎn)中的#Text */
- removeWhitespace(nav);//移除所有的空Text節(jié)點(diǎn)
- /*查找子節(jié)點(diǎn)*/
- var c_n_f=nav.firstChild;//***個(gè)節(jié)點(diǎn)對(duì)象
- //var c_n_f=nav.childNodes[0];//同上一樣的結(jié)果
- var c_n_l=nav.lastChild;//***一個(gè)節(jié)點(diǎn)對(duì)象
- //var c_n_l=nav.childNodes[nav.childNodes.length-1];//同上一樣的結(jié)果
- alert("***個(gè)節(jié)點(diǎn):"+c_n_f.nodeName+" "+"***一個(gè)節(jié)點(diǎn) :"+c_n_l.nodeName);
- /*查找兄弟節(jié)點(diǎn) 或叫 相鄰節(jié)點(diǎn) */
- /*用nextSibling和PreviousSibling必須有一個(gè)參考點(diǎn),這樣指針才知道自己往那里移動(dòng)*/
- var c_n_s=c_n_f.nextSibling;//***個(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
- alert("***個(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn):"+c_n_s.innerHTML+ "\n" + "節(jié)點(diǎn)中包含的HTML內(nèi)容: "+c_n_s.nodeName);
- }
- </script>
寫到這里,既然標(biāo)準(zhǔn)的previousSibling,nextSibling,firstChild,lastChild,parentNode遍歷方法有瀏覽器不兼容問題。我上面的解決方案是去掉遍歷元素的相關(guān)空的#Text節(jié)點(diǎn),是一個(gè)好的解決方案,但是使用起來不方便,我們何不自己寫一些遍歷節(jié)點(diǎn)的方法來代替標(biāo)準(zhǔn)的的previousSibling,nextSibling,firstChild,lastChild,parentNode。
我們的思路是利用元素是nodeType屬性來判斷元素是節(jié)點(diǎn)類型中那種節(jié)點(diǎn)類型,在DOM節(jié)點(diǎn)中我最常用的是元素節(jié)點(diǎn),文本節(jié)點(diǎn),屬性節(jié)點(diǎn),對(duì)應(yīng)的類型值是元素節(jié)點(diǎn)nodeType=1 or ELEMENT_NODE, 文本節(jié)點(diǎn) nodeType=2 or ATTRIBUTE_NODE,屬性節(jié)點(diǎn) nodeType=3 or TEXT_NODE,但是IE中并不支持命名常量,那就用數(shù)值吧,再配合標(biāo)準(zhǔn)的遍歷屬性。完全可以自己生產(chǎn)一些輔助函數(shù)來取代標(biāo)準(zhǔn)的遍歷方式。以下一系列的輔助函數(shù)可以幫助您,他們能取代標(biāo)準(zhǔn)的previousSibling,nextSibling,firstChild,lastChild,parentNode;
- <script type="text/javascript">
- //---------DOM 遍歷,如果元素沒找到則返回null---------//
- //---查找相關(guān)元素的前一個(gè)兄弟元素---//
- function prev(elem){
- do{
- elemelem=elem.previousSibling;
- }while(elem && elem.nodeType!=1);
- return elem;
- }
- //---查找相關(guān)元素的下一個(gè)兄弟元素---//
- function next(elem){
- do{
- elemelem=elem.nextSibling;
- }while(elem && elem.nodeType!=1);
- return elem;
- }
- //---查找***個(gè)子元素的函數(shù)---//
- function first(elem){
- elemelem=elem.firstChild;
- return elem && elem.nodeType!=1 ?next(elem):elem;
- }
- //---查找***一個(gè)子元素的函數(shù)---//
- function last(elem){
- elemelem=elem.lastChild;
- return elem && elem.nodeType!=1 ?prev(elem):elem;
- }
- //---查找父級(jí)元素的函數(shù)---//
- //num是父級(jí)元素的級(jí)次,parent(elem,2)等價(jià)于
- function parent(elem,num){
- numnum=num||1;
- for(var i=0; i<num; i++){
- if(elem!=null){
- elemelem=elem.parentNode;
- }
- }
- return elem;
- }
- </script>
原文鏈接:http://cssrainbow.cn/tutorials/javascript/565.html
【編輯推薦】