說說 JavaScript 在 DOM2 級標准中定義的遍歷規則


DOM2 級標准定義了兩個用於完成順序遍歷 DOM 結構的類型:NodeIterator 和 TreeWalker。它們都能夠基於給定的起點對 DOM 結構進行深度優先遍歷。與 DOM 兼容的瀏覽器(Firefox 1+、Safari 1.3+、Opera 7.6+、Chrome 0.2+)都支持這兩個類型。IE 不支持!以下代碼可以檢測瀏覽器是否支持 DOM2 級遍歷:

 var supportsTraversals = document.implementation.hasFeature("Traversal","2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
遍歷以給定的節點為根,不會超出 DOM 樹的根節點! 我們以下面的 HTML 頁面為例:
 <!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p><b>Hello</b> world!</p>
</body>
</html>

下面的圖示給出了深度遍歷的順序:

Markdown

1 NodeIterator

使用 document.createNodeIterator() 方法創建一個 NodeIterator 實例。它接受四個參數:

參數名 說明
root 樹中某個節點作為搜索起點。
whatToShow 要訪問哪些節點的數字代碼。
filter NodeFilter 對象,用於過濾某些節點的函數。
entityReferenceExpansion 布爾值,是否擴展實體引用。它在 HTML 中無用處。

whatToShow 是一個位掩碼,通過應用一個或者多個過濾器來確定要訪問的節點。它的值在 NodeFilter 類型中定義:

說明
NodeFilter.SHOW_ALL 顯示所有類型的節點。
NodeFilter.SHOW_ELEMENT 顯示元素節點。
NodeFilter.SHOW_ATTRIBUTE 顯示屬性節點。因為 DOM 結構沒有示屬性節點,所以實際上無用處。
NodeFilter.SHOW_TEXT 顯示文本節點。
NodeFilter.SHOW_CDATA_SECTION 顯示 CDATA 節點,HTML 中無用處。
NodeFilter.SHOW_ENTITY_REFERENCE 顯示實體引用節點,HTML 中無用處。
NodeFilter.SHOW_ENTITYE 顯示實體節點,HTML 中無用處。
NodeFilter.SHOW_PROCESSING_INSTRUCTION 顯示處理指令節點,HTML 中無用處。
NodeFilter.SHOW_COMMENT 顯示注釋節點。
NodeFilter.SHOW_DOCUMENT 顯示文檔節點。
NodeFilter.SHOW_DOCUMENT_TYPE 顯示文檔類型節點。
NodeFilter.SHOW_DOCUMENT_FRAGMENT 顯示文檔片段節點,HTML 中無用處。
NodeFilter.SHOW_NOTATION 顯示符號節點,,HTML 中無用處。

除了 NodeFilter.SHOW_ALL 之外,它們都可以按位或操作符來組合多個選項,比如:

var whatToShow = odeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;

可以使用 filter 參數來指定自定義的 NodeFilter 對象。每個 NodeFilter 對象有一個 acceptNode() 方法;如果應該訪問某個節點,就返回 NodeFilter.FILTER_ACCEPT;如果不應該訪問,就返回 NodeFilter.FITLER_SKIP。由於 NodeFilter 對象是抽象類型,所以只能創建一個包含 acceptNode() 方法的對象,然后將它傳給 createNodeIterator()。

現在創建一個只顯示 <p> 元素的節點迭代器:

  var filter = {
acceptNode: function(node){
return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FITLER_SKIP;
}
};

var iterator = document.createNodeIterator(root,NodeFilter.SHOW_ELEMENT,filter,false);
第三個參數也可以是一個與 acceptNode() 方法類似的函數:
 var filter = function(node){
return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FITLER_SKIP;
}
如果不指定過濾器,那么第三個參數傳入 null。 下面創建一個能夠訪問所有類型節點的 NodeIterator:
   var iterator = document.createNodeIterator(root,NodeFilter.SHOW_ALL,null,false);

NodeIterator 有兩個方法:

  • nextNode(),向前前進一步。
  • previousNode(),向后后退一步。

剛剛創建的 NodeIterator 對象,有一個內部指針指向根節點,所以第一次調用 nextNode() 會返回根節點,當遍歷到最后一個節點時,再調用 nextNode() 會返回 null。previousNode() 的機制與 nextNode() 類似。

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>遍歷某個元素中的所有元素(加上過濾器)</title>
</head>
<body>

<div id="div1">
<p><b>Hello</b> world!</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
</div>

<script type="text/javascript">
var div = document.getElementById("div1");

var filter = function (node) {
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};

var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while (node !== null) {
console.log(node.tagName);//輸出標簽名
node = iterator.nextNode();
}
</script>
</body>
</html>

如果只想返回 <li> 元素,可以加一個過濾器:

var div = document.getElementById("div1");

var filter = function (node) {
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};

var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while (node !== null) {
console.log(node.tagName);//輸出標簽名
node = iterator.nextNode();
}

注意: Firefox 3.5 之前的版本沒有實現 createNodeIterator() 方法!


2 TreeWalker

它除了包括 nextNode() 、previousNode() 之外,還包括這些方法:

方法名 說明
parentNode() 遍歷到當前節點的父節點
firstChild() 遍歷到當前節點的第一個子節點
lastChild() 遍歷到當前節點的最后一個子節點
nextSibling() 遍歷到當前節點的下一個同輩節點
previsousSibling() 遍歷到當前節點的上一個同輩節點

使用 document.createTreeWalker() 創建 TreeWalker 對象,它接受的 4 個參數與 createNodeIterator() 方法相同:

var div = document.getElementById("div1");

var filter = function (node) {
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};

var iterator = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while (node !== null) {
console.log(node.tagName);//輸出標簽名
node = iterator.nextNode();
}

在 TreeWalker 中,還可以返回 NodeFilter.FILTER_REJECT!它與 NodeFilter.FILTER_SKIP 不同之處是,NodeFilter.FILTER_SKIP 會跳過相應的節點,並繼續執行到子樹中的下一個節點,而 NodeFilter.FILTER_REJECT 會跳過相應的節點,並跳過這個節點的整個子樹!

TreeWalker 能夠在 DOM 結構中沿着任何方向移動,很厲害吧 O(∩_∩)O~。比如我們把之前的例子改造下,可以不用過濾器,就可以取得所有 <li> 元素:

var div = document.getElementById("div1");
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false);

walker.firstChild();//轉到 <p>
walker.nextSibling();//轉到 <ul>

var node = walker.firstChild();//轉到第一個 <li>
while (node !== null) {
console.log(node.tagName);
node = walker.nextSibling();
}

TreeWalker 有一個叫做 currentNode 的屬性,它表示的是,在上一次遍歷中返回的節點。通過修改它也可以修改當前遍歷繼續執行的起點,比如:

var node = walker.nextNode();
console.log(node ==== walker.currentNode);//true
walker.currentNode = document.body;//修改了起點

因為 IE 沒有對應的類型和方法,所以幾乎沒有跨瀏覽器的遍歷方案!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com