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节点。NodeIteratorTreeWalker都以这种方式执行遍历。

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_ACCEPTNodeFilter.FILTER_SKIP之外,还可以使用NodeFilter.FILTER_REJECT。在使用NodeIterator对象时,NodeFilter.FILTER_SKIPNodeFilter.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高级程序设计

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容