JavaScript DOM2 - 遍历

遍历

“DOM2级遍历和范围”模块定义了两个用于辅助完成顺序遍历DOM结构的类型: NodeIterator和TreeWalker。这两个类型能够基于给定的起点对DOM结构执行深度优先( depth-first )的遍历操作。

DOM遍历是深度优先的DOM结构遍历,也就是说,移动的方向至少有两个(取决于使用的遍历类型)。遍历以给定节点为根,不可能向上超出DOM树的根节点。
在这里插入图片描述

任何节点都可以作为遍历的根节点。如果假设元素为根节点,那么遍历的第一步 就是访问<p>元素,然后再访问同为<body>元素后代的两个文本节点。不过,这次遍历永远不会到达<html><head>元素,也不会到达不属于<body>元素子树的任何节点。而以document为根节点的遍历则可以访问文档中的全部节点。

下图展示了对以document为根节点的DOM树进行深度优先遍历的先后顺序。NodeIterator 和TreeWalker都以这种方式遍历。
在这里插入图片描述

1、NodeIterator

使用document.createNodeIterator() 方法来创建它的实例。这个方法接收下列4个参数:

  • root:想要作为搜索起点的数中的节点。
  • whatToShow:表示想要访问哪些节点的数字代码。
  • filter:是一个NodeFilter对象,或者一个表示应该接受还是某种特定节点的函数。(类似节点过滤器的函数)
  • entityReferenceExpansion:布尔值表示是否扩展实体引用。这个参数在HTML页面中没有用,因为其中的实体引用不能扩展。

whatToShow参数是一个位掩码, 通过应用- -或多 个过滤器( filter )来确定要访问哪些节点。这个参数的值以常量形式在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页面没有用。
  • NodeFil ter . SHOW_ ENTITYE:显示实体节点。对HTML页面没有用。
  • NodeFil ter . SHOW_ PROCESSING_ INSTRUCTION:显示处理指令节点。对HTML页面没有用。
  • NodeFilter . SHOW_ COMMENT:显示注释节点。
  • NodeFilter . SHOW_ DOCUMENT: 显示文档节点。
  • NodeFilter . SHOW_ DOCUMENT_TYPE: 显示文档类型节点。
  • NodeFil ter . SHOW_ DOCUMENT_ FRAGMENT:显示文档片段节点。对HTML页面没有用。
  • NodeFilter . SHOW_ NOTATION:显示符号节点。对HTML页面没有用。

除了NodeFilter.SHOW_ ALL之外,可以使用按位或操作符来组合多个选项,

var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;

可以通过createNodeIterator()方法的filter参数来指定自定义的NodeFilter对象,或者指定一个功能类似节点过滤器( node filter )的函数。
            var filter = function(node) {
                return node.tagName.toLowerCase() == "li";
                NodeFilter.FILTER_ACCEPT;
                NodeFilter.FILTER_SKIP;
            }

NodeIterator类型的两个主要方法是nextNode ()和previousNode()。顾名思义,在深度优先的DOM子树遍历中,nextNode ()方法用于向前前进一步,而previousNode ()用于向后后退一步。在刚刚创建的NodeIterator对象中,有一个内部指针指向根节点,因此第一次调用nextNode()会返回根节点。当遍历到DOM子树的最后一个节点时,nextNode() 返回null。previousNode()方法的工作机制类似。当遍历到DOM子树的最后一个节点,且previousNode()返回根节点之后,再次调用它就会返回null。


小例子:遍历指定节点中 <li>的元素,

            var mylist = document.getElementById("mylist");  //获取我们遍历起点的节点。
            
            var filter = function(node) {
                return node.tagName.toLowerCase() == "li"?
                NodeFilter.FILTER_ACCEPT:
                NodeFilter.FILTER_SKIP;
            }
            
            var iterator = document.createNodeIterator(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
            var node = iterator.nextNode;
            while (node !== null) {
                console.log(node.tagName)  //输出标签名
                node = iterator.nextNode();
            }

TrreWalker

TreeWalker是NodeIterator的一个更高级 的版本。除了包括 nextNode ()和previousNode ()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历DOM结构的方法。

  • parentNode():遍历到当前节点的父节点;
  • firstChild():遍历到当前节点的第一个子节点;
  • lastChild():遍历到当前节点的最后一个子节点;
  • nextSibling():遍历到当前节点的下一个同辈节点;
  • previoussibling():遍历到当前节点的上一个同辈节点。

创建Treewalker对象要使用document. createTreewalker()方法,这个方法接受的4个参数与document . createNodeIterator ()方法相同:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值。由于这两个创建方法很相似,所以很容易用Treewalker 来代替NodeIterator,如下面的例子所示。

var mylist = document.getElementById("mylist");
            var filter = function(node) {
                return node.tagName.toLowerCase() == "li" ?
                    NodeFilter.FILTER_ACCEPT :
                    NodeFilter.FILTER_SKIP;
                // NodeFilter.FILTER_REJECT;
            }
            var walker = document.createTreeWalker(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
            var node = walker.nextNode;
            while (node !== null) {
                console.log(node.tagName)  //输出标签名
                node = walker.nextNode();
            }

在这里,filter可以返回的值有所不同。除了NodeFilter .FILTER_ACCEPT 和NodeFilter.FILTER_ SKIP之外,还可以使用NodeFilter . FILTER_REJECT。 在使用NodeIterator对象时,NodeFilter . FILTER_ SKIP与NodeFilter . FILTER_ REJECT 的作用相同:跳过指定的节点。但在使用TreeWalker对象时,NodeFilter . FILTER SKIP会跳过相应节点继续前进到子树中的下一个节点,而NodeFilter . FILTER REJECT 则会跳过相应节点及该节点的整个子树。例如,将前面例子中的NodeFilter . FILTER_ SKIP 修改成NodeFilter. FILTER_ REJECT, 结果就是不会访问任何节点。这是因为第一个返回的节点是<div>,它的标签名不是"li",于是就会返回NodeFilter . FILTER REJECT ,这意味着遍历会跳过整个子树。在这个例子中,<div>元素是遍历的根节点,于是结果就会停止遍历。

当然,Treewalker真正强大的地方在于能够在DOM结构中沿任何方向移动。使用Treewalker遍历DOM树,即使不定义过滤器,也可以取得所有<li>元素,如下面的代码所示。(前提我们知道它的结构树)

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

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

            var node = walker.firstChild();
            while (node !== null) {
                console.log(node.tagName)    
                node = walker.nextSibling();
            }

它的HTML结构

    <div id="mylist">
        <p>sdfsdf</p>
        <ul>
            <li id="li"></li>
            <li></li>
            <li></li>
        </ul>
    </div>

TreeWalker类型 还有一个属性,名叫 currentNode ,表示任何遍历方法在上一次遍历中返回的节点。通过设置这个属性也可以修改遍历继续进行起点,如下:

            var node = walker.nextNode;
            console.log(node === walker.currentNode); //ture
            walker.currentNode = document.body; //修改起点

下面的例子会返回body中所有的 li 节点

            var mylist = document.getElementById("mylist");
            var filter = function(node) {
                return node.tagName.toLowerCase() == "li" ?
                    NodeFilter.FILTER_ACCEPT :
                    NodeFilter.FILTER_SKIP;
                // NodeFilter.FILTER_REJECT;
            }
            var walker = document.createTreeWalker(mylist, NodeFilter.SHOW_ELEMENT, filter, false);
            var node = walker.nextNode;
            console.log(node === walker.currentNode); //ture
            walker.currentNode = document.body; //修改起点
            while (node !== null) {
                console.log(node.tagName)
                node = walker.nextNode();
            }


愿你的坚持终有收获。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值