DOM 中 TreeWalker 對象的介紹(翻譯)
原文地址:http://www.javascriptkit.com/dhtmltutors/treewalker.shtml
TreeWalker對象是DOM2中提供的一個強大的工具,可以用來過濾文檔中的節(jié)點,以便于產(chǎn)生自定義的節(jié)點集合。這聽起來沒有什么太大的用處,但是如果你需要處理諸如遍歷DOM樹這樣的問題時,了解一下TreeWalker對象會帶來很大的幫助。你可能已經(jīng)很熟悉如何在Web頁面中查找具有某個CSS樣式名稱的節(jié)點集合,如何在XML文件中查找某個屬性為特定值的腳本寫法。借助TreeWalker,僅需少量的工作也可以完成類似功能。在本文中,我將向你介紹TreeWalker對象,需要注意的是,TreeWalker對象已經(jīng)在Firefox/Opera8+中支持,但是IE6、IE7尚不支持。(注:Chrome、Safari這些基于WebKit內(nèi)核的瀏覽器也支持TreeWalker對象,IE9+也已經(jīng)支持)
另外,和TreeWalker關(guān)系緊密的另外一個對象NodeIterator,也會在本文檔中涵蓋。
document.createTreeWalker()方法
對于某些人來說,TreeWalker對象開起來有點兒神秘并且很復雜。實際上,要想使用TreeWalker對象,只需一個方法:document.createTreeWalker()。此方法有4個參數(shù),可以完成大部分的常見需求,例如在文檔中查找某種類型或者具有某個屬性的節(jié)點。對于此方法簡單介紹如下:
document.createTreeWalker(root, nodesToShow, filter, entityExpandBol)
來了解一下這4個參數(shù):
root:文檔樹搜索的起始節(jié)點
nodesToShow:TreeWalker對象要訪問的節(jié)點類型
filter(or null):用來過濾返回結(jié)果的自定義函數(shù),null表示不使用自定義的過濾函數(shù)
entityExpandBol:是否展開實體引用
對于參數(shù)3,有以下可用的常量:
NodeFilter常量
NodeFilter.SHOW_ALL
NodeFilter.SHOW_ENTITY_REFERENCE
NodeFilter.SHOW_DOCUMENT_TYPE
NodeFilter.SHOW_ELEMENT
NodeFilter.SHOW_ENTITY
NodeFilter.SHOW_ENTITY
NodeFilter.SHOW_ATTRIBUTE
NodeFilter.SHOW_PROCESSING_INSTRUCTION
NodeFilter.SHOW_NOTATION
NodeFilter.SHOW_TEXT
NodeFilter.SHOW_COMMENT
NodeFilter.SHOW_CDATA_SECTION
NodeFilter.SHOW_DOCUMENT
雖然有如此多的常量可以用來限制TreeWalker返回的節(jié)點,但是在實際應(yīng)用中,可能常用的也就是其中的少數(shù)幾個常量。例如:NodeFilter.SHOW_ELEMENT返回所有的節(jié)點。
我們先從一個最基本的示例開始:
<div id="contentarea"><p>Some <span>text</span></p><b>Bold text</b></div><script type="text/javascript">var rootnode=document.getElementById("contentarea");var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);</script>
在這個示例中,createTreeWalker方法的root參數(shù)為ID是contentarea的元素,讓TreeWalker對象以這個節(jié)點為根開始進行遍歷。第二個參數(shù)限制TreeWalker只遍歷根節(jié)點下的“元素”節(jié)點(例如忽略文本節(jié)點和注釋節(jié)點)。第三個參數(shù)設(shè)置為null表示不需要引入自定義的過濾器。第四個參數(shù),用來控制實體引用是否被展開,這里我們設(shè)置為false。這段代碼執(zhí)行完畢之后,walker對象指向了包含DIV自己在內(nèi)的以及DIV下的所有子元素節(jié)點(P, SPAN, B)。
TreeWalker的遍歷方法
使用document.createTreeWalker()方法創(chuàng)建了過濾后的節(jié)點列表,然后可以使用TreeWalker的遍歷方法對這些節(jié)點進行遍歷:
方法 | 描述 |
---|
firstChild() | 返回當前節(jié)點的第一個子節(jié)點 |
lastChild() | 返回當前節(jié)點的最后一個子節(jié)點 |
nextNode() | 返回過濾后的節(jié)點列表中的下一個節(jié)點 |
nextSibling() | 返回當前節(jié)點的下一個兄弟節(jié)點 |
parentNode() | 返回當前節(jié)點的父節(jié)點 |
previousNode() | 返回過濾后的節(jié)點列表中的上一個節(jié)點 |
previousSibling() | 返回當前節(jié)點的上一個兄弟節(jié)點 |
屬性 | 描述 |
---|
currentNode | 返回TreeWalker對象的當前位置或者當前節(jié)點。
這是一個可讀/寫屬性,可以通過設(shè)置此屬性,讓TreeWalker指向某個特定的節(jié)點。 |
不要把上述的這些方法和屬性和標準DOM元素的方法和屬性混淆,以上的方法只用在TreeWalker對象中,以實現(xiàn)遍歷過濾后的節(jié)點集合的能力。
還是使用上面的示例代碼,這次,我們加入一些代碼來遍歷TreeWalker返回的節(jié)點列表:
<div id="contentarea"><p>Some <span>text</span></p><b>Bold text</b></div><script type="text/javascript">var rootnode=document.getElementById("contentarea");var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);alert(walker.currentNode.tagName); while (walker.nextNode()) alert(walker.currentNode.tagName); walker.currentNode=rootnode
alert(walker.firstChild().tagName); </script>
當你使用TreeWalker的遍歷方法時,TreeWalker不僅依次返回過濾后的節(jié)點,同時它還移動了當前指向節(jié)點的指針,所以,在使用while (walker.nextNode())
完成遍歷之后,還要使用walker.currentNode=rootnode
重置它的當前節(jié)點指向根節(jié)點,以便獲取到第一個子元素。
再來一個示例加深一下對TreeWalker遍歷的理解:
<p id="essay">George<span> loves </span> <b>JavaScript!</b></p><script type="text/javascript">var rootnode=document.getElementById("essay");var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_TEXT, null, false);
walker.firstChild(); var paratext=walker.currentNode.nodeValue;while (walker.nextSibling()){
paratext+=walker.currentNode.nodeValue;
}alert(paratext); </script>
在這個示例中,我們遍歷了根節(jié)點下所有的文本節(jié)點以獲取它完整的文本字符串。
在遍歷TreeWalker的返回結(jié)果時,你也可以使用標準DOM元素的屬性和方法。因為TreeWalker的返回值不僅僅返回了過濾后的節(jié)點,還包括這些節(jié)點在整個文檔中的關(guān)系。比如下面這個示例:
<ul id="mylist"><li>List 1</li><li>List 2</li><li>List 3</li></ul><script type="text/javascript">var rootnode=document.getElementById("mylist");var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);alert(walker.currentNode.childNodes.length); alert(walker.currentNode.getElementsByTagName("*").length); </script>
這個示例中,使用TreeWalker查找UL節(jié)點下的所有元素。你可能會誤以為alert(walker.currentNode.childNodes.length)
會返回3,因為UL只有3個LI子元素。但是實際上計算上文本節(jié)點的話,UL元素就包含7個子元素了,這就是為什么上面的代碼會返回7。
了解了如何遍歷TreeWalker的返回節(jié)點列表之后,下面將介紹如何自定義過濾器。還記得document.createTreeWalker()
函數(shù)的第三個參數(shù)嗎?我們將這個參數(shù)指向一個自定義的函數(shù)來完成自定義過濾器的功能。
在document.createTreeWalker()中使用過濾器
TreeWalker對象的本質(zhì)是提供一種在文檔中過濾節(jié)點的能力。在前面的內(nèi)容中,我們已經(jīng)看到了可以使用NodeFilter的各種常量(例如NodeFilter.SHOW_ELEMENT)來完成最基本的過濾功能。但是在實際的場景中,這些常量可能還不足以支持你完成你的需求。這就需要用到document.createTreeWalker()
函數(shù)的第三個參數(shù),這個參數(shù)允許你自定義一個過濾函數(shù)來完成自定義的過濾,也就是說,對于第二個參數(shù)所指定的常量產(chǎn)生的結(jié)果再次進行過濾。
document.createTreeWalker(root, nodesToShow, filter, entityExpandBol)
"filter"參數(shù)指向一個函數(shù),例如:
var myfilter=function(node){ if (node.tagName=="DIV" || node.tagName=="IMG")
return NodeFilter.FILTER_ACCEPT; else
return NodeFilter.FILTER_SKIP;
};var walker=document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, myfilter, false);while (walker.nextNode())
walker.currentNode.style.display="none";
在上面的示例代碼中,我們定義了一個叫做myfilter的變量,該變量指向一個函數(shù),這個函數(shù)將僅保留DIV和IMG元素,而把其他的元素排除在外。作為過濾器的函數(shù)只接收一個參數(shù),就是TreeWalker在遍歷整個文檔時當前所指向的節(jié)點。在過濾器函數(shù)中,你可以使用不同的返回值來實現(xiàn)接受、拒絕還是跳過當前的節(jié)點:
NodeFilter.FILTER_ACCEPT
NodeFilter.FILTER_REJECT
NodeFilter.FILTER_SKIP
不言自明,F(xiàn)ILTER_ACCEPT就是表示接受這個節(jié)點,將其包含到返回的結(jié)果中。但是FILTER_REJECT和FILTER_SKIP的含義可能會有些不那么明顯了。對于FILTER_REJECT,TreeWalker將拒絕當前節(jié)點以及其所有的后代節(jié)點,也就是說,當你的過濾器函數(shù)返回FILTER_REJECT的時候,TreeWalker將不再遍歷該節(jié)點下的所有后代節(jié)點。如果你需要僅僅過濾掉當前節(jié)點,并且也希望TreeWalker繼續(xù)遍歷該節(jié)點下的所有后代節(jié)點,那么請使用NodeFilter.FILTER_SKIP。例如對于上面的例子中,如果把 FILTER_SKIP 改為 FILTER_REJECT:
var myfilter=function(node){if (node.tagName=="DIV" || node.tagName=="IMG")
return NodeFilter.FILTER_ACCEPT;else
return NodeFilter.FILTER_REJECT;
};
這樣的會導致返回的結(jié)果中可能并沒有包含文檔中全部的DIV和IMG元素,因為如果一個IMG元素作為一個P元素的子元素的話,那么由于P元素被返回了FILTER_REJECT,那么P元素下的IMG元素也不會被TreeWalker遍歷。
示例:根據(jù)class屬性操作元素
在下面這個示例中,使用TreeWalker對象查找文檔中的所有class為blue的元素,并將其class設(shè)置為red:
var getelementbyclass=function(node){if (node.className=="blue")
return NodeFilter.FILTER_ACCEPT;else
return NodeFilter.FILTER_SKIP;
};var rootnode=document.body;var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, getelementbyclass, false);while (walker.nextNode())
walker.currentNode.style.color="red";
組合使用NodeFilter常量
在前面的內(nèi)容中我們已經(jīng)了解到NodeFilter提供了很多常量來讓我們獲取某種類型的節(jié)點,這些常量也可以組合使用,例如:
OR 操作:NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
AND 操作:NodeFilter.SHOW_TEXT + NodeFilter.SHOW_COMMENT
NOT 操作:~NodeFilter.SHOW_COMMENT (獲取所有的非注釋節(jié)點)
只遍歷所有的元素節(jié)點和文本節(jié)點:
document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, entityExpandBol);
這就是DOM2中提供的TreeWalker對象。請記住,并不是所有的瀏覽器都支持此對象。