前端开发 \ JavaScript \ 说说 JavaScript 在 DOM2 级标准中定义的遍历规则

说说 JavaScript 在 DOM2 级标准中定义的遍历规则

总点击199
简介:DOM2级标准定义了两个用于完成顺序遍历DOM结构的类型:NodeIterator和TreeWalker。它们都能够基于给定的起点对DOM结构进行深度优先遍历。与DOM兼容的浏览器(Firefox1+、Safari1.3+、Opera7.6+、Chrome0.2+)都支持这

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>

下面的图示给出了深度遍历的顺序:

说说 JavaScript 在 DOM2 级标准中定义的遍历规则

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,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,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,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,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 没有对应的类型和方法,所以几乎没有跨浏览器的遍历方案!

意见反馈 常见问题 官方微信 返回顶部