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