zoukankan      html  css  js  c++  java
  • JS之DOM篇节点遍历

    定义

    DOM遍历模块定义了用于辅助完成顺序遍历DOM结构的类型:Nodeiterator和TreeWalker,它们能够基于给定的起点对DOM结构执行深度优先(depth-first)的遍历操作

    示例HTML

    <!DOCTYPE html>
    <html>
      <head>
        <title>Example</title>
      </head>
      <body>
        <p><b>Hello</b> world!</p>
      </body>
    </html>
    

    示例HTML的DOM结构

    以document为根节点的DOM树进行深度优先遍历的先后顺序

    任何节点都可以作为遍历的根节点,示例展示了以document为根节点的DOM树进行深度优先遍历的先后顺序,访问的第一个节点是document节点,最后一个节点是包含'world!'的文本节点

    从文档最后的文本节点开始,遍历可以反向移动到DOM树的顶端,此时访问的第一个节点是包含"Hello"的文本节点,访问的最后一个节点是document节点。Nodeiterator和TreeWalker都以这种方式执行遍历

    NodeIterator()

    document.NodeIterator()方法可以创建NodeIterator类型的新实例,该方法接收4个参数

    root: 搜索起点
    whatToShow: 数字代码,表示要访问哪些节点
    filter: NodeFilter对象,或者一个表示应该接受还是拒绝某种节点的函数
    entityReferenceExpansion: 布尔值,表示是否要扩展实体引用。由于在HTML页面中,实体引用不能扩展,所以这个参数在HTML页面中没有用
    

    whatToShow参数是一个位掩码,这个参数的值以常量形式在NodeFilter类型中定义,可以通过应用一或多个过滤器(filter)来确定要访问哪些节点

    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.SH0W_PROCESSING_INSTRUCTION: 显示处理指令节点。对HTML页面没有用
    NodeFi1ter.SHOW_COMMENT: 显示注释节点
    NodeFilter.SHOW_DOCUMENT: 显示文档节点
    NodeFilter.SHOW_DOCUMENT_TYPE: 显示文档类型节点
    NodeFilter.SHOW_DOCUMENT_FRAGMENT: 显示文档片段节点。对HTML页面没有用
    NodeFilter.SHOW_NOTATION: 显示符号节点。对HTML页面没有用
    

    创建一个只显示<p>元素的节点迭代器

    var filter = {
      acceptNode: function(node) {
        return node.tagName.toLowerCase() === 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
      }
    }
    
    var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false)
    

    通过createNodeIterator()方法的filter参数,可以自定义NodeFilter对象,每个NodeFilter对象有且只有一个acceptNode()方法,如果应该访问给定的节点,该方法返回NodeFilter.FILTER_ACCEPT,如果不应该访问给定的节点,该方法返回NodeFilter.FILTER_SKIP。由于NodeFilter是一个抽象类型,因此不能直接创建它的实例,可以像示例中一样,创建一个包含acceptNode()方法的对象,然后传给createNodeIterator()方法

    filter也可以是一个与acceptNode()方法类似的函数

    var filter = function(node) {
      return node.tagName.toLowerCase() === 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
    }
    
    var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false)
    

    如果不指定过滤器,那么应该在第三个参数的位置上传入null

    var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false)
    

    NodeIterator类型有两个主要的方法:nextNode()和previousNode()。在深度优先的DOM树遍历中,nextNode()方法用于向前前进一步,previousNode()用于向后后退一步

    <div id="box">
      <p>列表</p>
      <ul>
        <li>one</li>
        <li>two</li>
      </ul>
    </div>
    <script>
      var iterator = document.createNodeIterator(box, NodeFilter.SHOW_ELEMENT, null, false)
      var node = iterator.nextNode()
      while(node !== null) {
        console.log(node.tagName)
        node = iterator.nextNode()
      }
      // DIV
      // P
      // UL
      // LI
      // LI
    </script>
    

    这个示例遍历了box元素的所有子元素,第一调用nextNode()方法会返回<p>元素,由于在到达DOM树末端时nextNode()返回null,所以可以用while语句检测每次循环时node值是否等于null

    如果只想获取<li>元素,可以加一个过滤器

    <div id="box">
      <p>列表</p>
      <ul>
        <li>one</li>
        <li>two</li>
      </ul>
    </div>
    <script>
      var filter = function(node) {
        return node.tagName.toLowerCase() === 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
      }
      var iterator = document.createNodeIterator(box, NodeFilter.SHOW_ELEMENT, filter, false)
      var node = iterator.nextNode()
      while(node !== null) {
        console.log(node.tagName)
        node = iterator.nextNode()
      }
      // LI
      // LI
    </script>
    

    由于nextNode()和previousNode()方法都基于NodeIterator在DOM结构中的内部指针工作,所以DOM结构的变化会反映在遍历的结果中

    TreeWalker

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

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

    document.createTreeWalker()方法可以创建TreeWalker对象,它接受和document.createNodelterator()方法相同的4个参数。下面使用TreeWalker代替NodeIterator遍历box元素节点

    <div id="box">
      <p>列表</p>
      <ul>
        <li>one</li>
        <li>two</li>
      </ul>
    </div>
    <script>
      var filter = function(node) {
        return node.tagName.toLowerCase() === 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
      }
      var iterator = document.createTreeWalker(box, NodeFilter.SHOW_ELEMENT, filter, false)
      var node = iterator.nextNode()
      while(node !== null) {
        console.log(node.tagName)
        node = iterator.nextNode()
      }
      // LI
      // LI
    </script>
    

    TreeWalker的filter参数返回值与NodeIterator的有所不同,它可以返回NodeFilter.FILTER_ACCEPT、NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT三种值。在使用TreeWalker对象时,NodeFilter.FILTER_SKIP表示跳过相应节点继续前进到子树中的下一个节点,NodeFilter.FILTER_REJECT表示跳过指定的节点。也就是说,在TreeWalker对象中使用NodeFilter.FILTER_REJECT与在NodeIterator对象中使用NodeFilter.FILTER_SKIP效果是相同的;在NodeIterator对象中,也可以返回NodeFilter.FILTER_REJECT,只不过NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT结果是一样的

    由于TreeWalker能够在DOM结构中沿任何方向移动,所以即使不定义过滤器,也可以取得所有<li>元素

    <div id="box">
      <p>列表</p>
      <ul>
        <li>one</li>
        <li>two</li>
      </ul>
    </div>
    <script>
      var walker = document.createTreeWalker(box, 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.nextNode()
      }
      // LI
      // LI
    </script>
    

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

    var node = walker.nextNode()
    console.log(node === walker.currentNode) // true
    walker.currentNode = document.body // 修改起点
    
    优秀文章首发于聚享小站,欢迎关注!
  • 相关阅读:
    addEventListener事件委托
    ES6的解构赋值
    JavaScript 中最​​重要的保留字
    HTML 5 Web 存储
    Node.js 里的 process.nextTick(),简单理解
    实现多层DIV叠加的js事件穿透
    touch事件中的touches、targetTouches和changedTouches详解
    Promise.all( ) 的使用
    js 中的五种迭代方法
    迭代
  • 原文地址:https://www.cnblogs.com/yesyes/p/15352507.html
Copyright © 2011-2022 走看看