• DOM 中 TreeWalker 对象的介绍(翻译)


    DOM 中 TreeWalker 对象的介绍(翻译)

    原文地址:http://www.javascriptkit.com/dhtmltutors/treewalker.shtml

    TreeWalker对象是DOM2中提供的一个强大的工具,可以用来过滤文档中的节点,以便于产生自定义的节点集合。这听起来没有什么太大的用处,但是如果你需要处理诸如遍历DOM树这样的问题时,了解一下TreeWalker对象会带来很大的帮助。你可能已经很熟悉如何在Web页面中查找具有某个CSS样式名称的节点集合,如何在XML文件中查找某个属性为特定值的脚本写法。借助TreeWalker,仅需少量的工作也可以完成类似功能。在本文中,我将向你介绍TreeWalker对象,需要注意的是,TreeWalker对象已经在Firefox/Opera8+中支持,但是IE6、IE7尚不支持。(注:Chrome、Safari这些基于WebKit内核的浏览器也支持TreeWalker对象,IE9+也已经支持)

    另外,和TreeWalker关系紧密的另外一个对象NodeIterator,也会在本文档中涵盖。

    document.createTreeWalker()方法

    对于某些人来说,TreeWalker对象开起来有点儿神秘并且很复杂。实际上,要想使用TreeWalker对象,只需一个方法:document.createTreeWalker()。此方法有4个参数,可以完成大部分的常见需求,例如在文档中查找某种类型或者具有某个属性的节点。对于此方法简单介绍如下:

    document.createTreeWalker(root, nodesToShow, filter, entityExpandBol)

    来了解一下这4个参数:

    1. root:文档树搜索的起始节点
    2. nodesToShow:TreeWalker对象要访问的节点类型
    3. filter(or null):用来过滤返回结果的自定义函数,null表示不使用自定义的过滤函数
    4. entityExpandBol:是否展开实体引用

    对于参数3,有以下可用的常量:

    NodeFilter常量

    • NodeFilter.SHOW_ALL
    • NodeFilter.SHOW_ENTITY_REFERENCE
    • NodeFilter.SHOW_DOCUMENT_TYPE
    • NodeFilter.SHOW_ELEMENT
    • NodeFilter.SHOW_ENTITY
    • NodeFilter.SHOW_ENTITY
    • NodeFilter.SHOW_ATTRIBUTE
    • NodeFilter.SHOW_PROCESSING_INSTRUCTION
    • NodeFilter.SHOW_NOTATION
    • NodeFilter.SHOW_TEXT
    • NodeFilter.SHOW_COMMENT
    • NodeFilter.SHOW_CDATA_SECTION
    • NodeFilter.SHOW_DOCUMENT

    虽然有如此多的常量可以用来限制TreeWalker返回的节点,但是在实际应用中,可能常用的也就是其中的少数几个常量。例如:NodeFilter.SHOW_ELEMENT返回所有的节点。

    我们先从一个最基本的示例开始:

    <div id="contentarea">
    <p>Some <span>text</span></p>
    <b>Bold text</b>
    </div>
    
    <script type="text/javascript">
    
    var rootnode=document.getElementById("contentarea");
    var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);
    
    </script>
    

    在这个示例中,createTreeWalker方法的root参数为ID是contentarea的元素,让TreeWalker对象以这个节点为根开始进行遍历。第二个参数限制TreeWalker只遍历根节点下的“元素”节点(例如忽略文本节点和注释节点)。第三个参数设置为null表示不需要引入自定义的过滤器。第四个参数,用来控制实体引用是否被展开,这里我们设置为false。这段代码执行完毕之后,walker对象指向了包含DIV自己在内的以及DIV下的所有子元素节点(P, SPAN, B)。

    TreeWalker的遍历方法

    使用document.createTreeWalker()方法创建了过滤后的节点列表,然后可以使用TreeWalker的遍历方法对这些节点进行遍历:

    方法描述
    firstChild() 返回当前节点的第一个子节点
    lastChild() 返回当前节点的最后一个子节点
    nextNode() 返回过滤后的节点列表中的下一个节点
    nextSibling() 返回当前节点的下一个兄弟节点
    parentNode() 返回当前节点的父节点
    previousNode() 返回过滤后的节点列表中的上一个节点
    previousSibling() 返回当前节点的上一个兄弟节点

     

    属性描述
    currentNode

    返回TreeWalker对象的当前位置或者当前节点。

    这是一个可读/写属性,可以通过设置此属性,让TreeWalker指向某个特定的节点。

    不要把上述的这些方法和属性和标准DOM元素的方法和属性混淆,以上的方法只用在TreeWalker对象中,以实现遍历过滤后的节点集合的能力。

    还是使用上面的示例代码,这次,我们加入一些代码来遍历TreeWalker返回的节点列表:

    <div id="contentarea">
    <p>Some <span>text</span></p>
    <b>Bold text</b>
    </div>
    
    <script type="text/javascript">
    
    var rootnode=document.getElementById("contentarea");
    var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);
    
    //TreeWalker当前指向的节点,也是它开始遍历的根节点(root参数指向的节点)
    alert(walker.currentNode.tagName); //alerts DIV (with id=contentarea)
    
    //遍历,显示所有的子节点
    while (walker.nextNode())
        alert(walker.currentNode.tagName); //alerts P, SPAN, and B.
    
    //重置TreeWalker的指向,让它指向根节点
    walker.currentNode=rootnode 
    alert(walker.firstChild().tagName); //alerts P
    
    </script>
    

    当你使用TreeWalker的遍历方法时,TreeWalker不仅依次返回过滤后的节点,同时它还移动了当前指向节点的指针,所以,在使用while (walker.nextNode())完成遍历之后,还要使用walker.currentNode=rootnode重置它的当前节点指向根节点,以便获取到第一个子元素。

    再来一个示例加深一下对TreeWalker遍历的理解:

    <p id="essay">George<span> loves </span> <b>JavaScript!</b></p>
    
    <script type="text/javascript">
    
    var rootnode=document.getElementById("essay");
    var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_TEXT, null, false);
    
    walker.firstChild(); //Walk to first child node (the text "George")
    var paratext=walker.currentNode.nodeValue;
    
    while (walker.nextSibling()){ //Step through each sibling of "George"
        paratext+=walker.currentNode.nodeValue;
    }
    
    alert(paratext); //alerts "George loves JavaScript!"
    
    </script>
    

    在这个示例中,我们遍历了根节点下所有的文本节点以获取它完整的文本字符串。

    在遍历TreeWalker的返回结果时,你也可以使用标准DOM元素的属性和方法。因为TreeWalker的返回值不仅仅返回了过滤后的节点,还包括这些节点在整个文档中的关系。比如下面这个示例:

    <ul id="mylist">
    <li>List 1</li>
    <li>List 2</li>
    <li>List 3</li>
    </ul>
    
    <script type="text/javascript">
    
    var rootnode=document.getElementById("mylist");
    var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, null, false);
    
    alert(walker.currentNode.childNodes.length); //alerts 7 (includes text nodes)
    alert(walker.currentNode.getElementsByTagName("*").length); //alerts 3
    
    </script>
    

    这个示例中,使用TreeWalker查找UL节点下的所有元素。你可能会误以为alert(walker.currentNode.childNodes.length)会返回3,因为UL只有3个LI子元素。但是实际上计算上文本节点的话,UL元素就包含7个子元素了,这就是为什么上面的代码会返回7。

    了解了如何遍历TreeWalker的返回节点列表之后,下面将介绍如何自定义过滤器。还记得document.createTreeWalker()函数的第三个参数吗?我们将这个参数指向一个自定义的函数来完成自定义过滤器的功能。

    在document.createTreeWalker()中使用过滤器

    TreeWalker对象的本质是提供一种在文档中过滤节点的能力。在前面的内容中,我们已经看到了可以使用NodeFilter的各种常量(例如NodeFilter.SHOW_ELEMENT)来完成最基本的过滤功能。但是在实际的场景中,这些常量可能还不足以支持你完成你的需求。这就需要用到document.createTreeWalker()函数的第三个参数,这个参数允许你自定义一个过滤函数来完成自定义的过滤,也就是说,对于第二个参数所指定的常量产生的结果再次进行过滤。

    document.createTreeWalker(root, nodesToShow, filter, entityExpandBol)
    

    "filter"参数指向一个函数,例如:

    var myfilter=function(node){
        if (node.tagName=="DIV" || node.tagName=="IMG") //只保留DIV和IMG元素
            return NodeFilter.FILTER_ACCEPT;
        else
            return NodeFilter.FILTER_SKIP;
        };
    
    var walker=document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, myfilter, false);
    
    while (walker.nextNode())
        walker.currentNode.style.display="none"; //隐藏页面中所有的DIV和IMG元素
    

    在上面的示例代码中,我们定义了一个叫做myfilter的变量,该变量指向一个函数,这个函数将仅保留DIV和IMG元素,而把其他的元素排除在外。作为过滤器的函数只接收一个参数,就是TreeWalker在遍历整个文档时当前所指向的节点。在过滤器函数中,你可以使用不同的返回值来实现接受、拒绝还是跳过当前的节点:

    • NodeFilter.FILTER_ACCEPT
    • NodeFilter.FILTER_REJECT
    • NodeFilter.FILTER_SKIP

    不言自明,FILTER_ACCEPT就是表示接受这个节点,将其包含到返回的结果中。但是FILTER_REJECT和FILTER_SKIP的含义可能会有些不那么明显了。对于FILTER_REJECT,TreeWalker将拒绝当前节点以及其所有的后代节点,也就是说,当你的过滤器函数返回FILTER_REJECT的时候,TreeWalker将不再遍历该节点下的所有后代节点。如果你需要仅仅过滤掉当前节点,并且也希望TreeWalker继续遍历该节点下的所有后代节点,那么请使用NodeFilter.FILTER_SKIP。例如对于上面的例子中,如果把 FILTER_SKIP 改为 FILTER_REJECT:

    var myfilter=function(node){
    if (node.tagName=="DIV" || node.tagName=="IMG") //filter out DIV and IMG elements
        return NodeFilter.FILTER_ACCEPT;
    else
        return NodeFilter.FILTER_REJECT;
    };
    

    这样的会导致返回的结果中可能并没有包含文档中全部的DIV和IMG元素,因为如果一个IMG元素作为一个P元素的子元素的话,那么由于P元素被返回了FILTER_REJECT,那么P元素下的IMG元素也不会被TreeWalker遍历。

    示例:根据class属性操作元素

    在下面这个示例中,使用TreeWalker对象查找文档中的所有class为blue的元素,并将其class设置为red:

    var getelementbyclass=function(node){
    if (node.className=="blue") //filter out elements with this class attribute
        return NodeFilter.FILTER_ACCEPT;
    else
        return NodeFilter.FILTER_SKIP;
    };
    
    var rootnode=document.body;
    var walker=document.createTreeWalker(rootnode, NodeFilter.SHOW_ELEMENT, getelementbyclass, false);
    
    while (walker.nextNode())
        walker.currentNode.style.color="red";
    

    组合使用NodeFilter常量

    在前面的内容中我们已经了解到NodeFilter提供了很多常量来让我们获取某种类型的节点,这些常量也可以组合使用,例如:

    • OR 操作:NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
    • AND 操作:NodeFilter.SHOW_TEXT + NodeFilter.SHOW_COMMENT
    • NOT 操作:~NodeFilter.SHOW_COMMENT (获取所有的非注释节点)

    只遍历所有的元素节点和文本节点:

    document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, entityExpandBol);
    

    这就是DOM2中提供的TreeWalker对象。请记住,并不是所有的浏览器都支持此对象。

  • 相关阅读:
    计算器代码
    acm数论之旅(转载)---最大公约数与最小公倍数
    acm数论之旅(转载) -- 快速幂
    acm数论之旅(转载)--素数
    位运算符(转载)
    最短路问题
    并查集
    深度优先探索与广度优先探索
    ACM注意事项
    LTE Module User Documentation(翻译6)——物理误差模型、MIMO模型、天线模型
  • 原文地址:https://www.cnblogs.com/yuanyq/p/3115143.html
Copyright © 2020-2023  润新知