说说 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要访问哪些节点的数字代码。
filterNodeFilter 对象,用于过滤某些节点的函数。
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 没有对应的类型和方法,所以几乎没有跨浏览器的遍历方案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值