JS浅谈遍历
先上dom结构
<!DOCTYPE html> <html> <head> <title>Example</title> </head> <body> <p><b>Hello</b> world!</p> </body> </html>
以document为根节点的DOM树进行深度优先遍历的先后顺序
遍历树
从document
开始依序向前,访问的第一个节点是document
,访问的最后一个节点是包含"world!"的文本节点。从文档最后的文本节点开始,遍历可以反向移动到DOM 树的顶端。此时,访问的第一个节点是包含"Hello"的文本节点,访问的最后一个节点是document
节点。NodeIterator
和TreeWalker
都以这种方式执行遍历。
NodeIterator
使用document.createNodeIterator()
方法创建它的新实例。该方法接受4个参数。
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 页面没有用。
filter
有两种方法实现filter,只显示<p>元素节点为例
先说一下三元运算:a===b?a:b 表示,===全等运算,a的值和类型全都等于b的值和类型吗,若为true,则返回a,否则返回b
法1:
var filter={acceptNode:function(node){ return node.tagName.toLowerCase()==='p'?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP; } }
法2:
var filter=function(node){return node.tagName.toLowerCaser()==='p'?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP; }
NodeIterator类型的两个主要方法是:nextNode()
和previousNode()
.
在深度优先的DOM 子树遍历中,nextNode()
方法用于向前前进一步,而previousNode()
用于向后后退一步。在新创建的NodeIterator
对象中,有一个内部指针指向根节点,因此第一次调用nextNode()
会返回根节点。当遍历到DOM 子树的最后一个节时,nextNode()
返回null
。
以下面的HTML 片段为例。遍历div下所有节点
<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>
var div=document.getElementById('div1');var iterator=document.createNodeIterator(div,NodeFilter.SHOW_ELEMENT,null,false);var node=iterator.nextNode();//指向第一个节点:id='div1'的那个节点while(node!==null){ console.log(node.tagName); node=iterator.nextNode(); }//输出DIV P B UL LI LI LI
TreeWalker
TreeWalker 是NodeIterator 的一个更高级的版本。除了包括nextNode()和previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历DOM 结构的方法。
parentNode():遍历到当前节点的父节点;
firstChild():遍历到当前节点的第一个子节点;
lastChild():遍历到当前节点的最后一个子节点;
nextSibling():遍历到当前节点的下一个同辈节点;
previousSibling():遍历到当前节点的上一个同辈节点。
创建TreeWalker 对象要使用document.createTreeWalker()
方法,这个方法接受的4 个参数与document.createNodeIterator()
方法相同:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值。
例如遍历li元素
var div = document.getElementById("div1");var filter = function(node) { return node.tagName.toLowerCase() == 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; }var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, filter, false);var node = walker.nextNode();//createTreeWalker调用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
,结果就是不会访问任何节点。
当然,TreeWalker
真正强大的地方在于能够在DOM
结构中沿任何方向移动。使用 TreeWalker
遍历DOM
树,即使不定义过滤器,也可以取得所有<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) { alert(node.tagName); node = walker.nextSibling(); }
例子
写一个traverse函数,输出所有页面宽度和高度大于50像素的节点。
var filter=function(node){ var style= document.defaultView.getComputedStyle instanceof Function?document.defaultView.getComputedStyle(node,null):node.currentStyle; if(style.width.slice(0,-2)>50){ // console.log(style.width) return NodeFilter.FILTER_ACCEPT; }else{ return NodeFilter.FITER_SKIP; } };var div=document.getElementById('div'); var iterator=document.createNodeIterator(div,NodeFilter.SHOW_ELEMENT,filter,false) var node=iterator.nextNode(); while(node!=null){ console.log(node.tagName); node=iterator.nextNode(); }
注意:获取元素样式:div.style.用于获取行内样式。获取其他样式表使用
document.default.getComputeStyle()方法
,该方法接受2个参数,第一个是要计算样式的元素,第二个是伪类,若不需要,则为null。低版本的IE要使用元素的currentStyle
属性,例如div.currentStyle.color
参考资料:JavaScript高级程序设计
作者:fenerchen
链接:https://www.jianshu.com/p/32e2e8742148
共同学习,写下你的评论
评论加载中...
作者其他优质文章