猿问

不排除元素的 DOM 遍历

我正在创建一个简单的脚本,它遍历 DOM 并返回一个树对象,其中包含在 DOM 中找到的元素。递归遍历本身非常简单,但我想要/需要跳过某些元素并包含其他元素。我该怎么做呢?


这是我的 HTML:


<div data-element="from-here">

  <div>skip me</div>

  <div>

    <div data-element="awesome">awesome text</div>

    <div data-element="collect-me">

      awesome text

      <div data-element="also-me">

        other text

        <div class="but-not-me">...</div>

      </div>

    </div>

  </div>

</div>

还有我的递归遍历代码:


const root = document.querySelector('[data-element="from-here"]');


function traverse(node) {

  return {

    element: node.dataset.element,

    children: Array.from(node.querySelectorAll(':scope > div')).map(childNode => traverse(childNode)),

  };

}


traverse(root);

正如您所看到的,代码查询所有 div 元素,但我只需要带有data-element属性的元素。我不能只执行 `node.querySelectorAll(':scope > [data-element]') 因为这不会超出第一个 div。


这就是我想要的结果:


{

  element: 'from-here',

  children: [

    {

      element: 'awesome',

      children: [],

    },

    {

      element: 'collect-me',

      children: [

        {

          element: 'also-me',

          children: [],

        }

      ]

    }

  ]

}

任何帮助将不胜感激!


三国纷争
浏览 117回答 3
3回答

郎朗坤

使用Array.prototype.flatMap可以大大降低转换的复杂性 -如果节点没有值data-element,不包含节点;仅包括其子项的结果。否则(通过归纳)该节点确实具有data-element值。将其包含element在输出中以及该节点的children.上面的编号点对应于下面的源评论 -const root = document.querySelector('[data-element="from-here"]');const toTree = ({ dataset = {}, children = [] }) =>&nbsp; dataset.element === undefined&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 1&nbsp; &nbsp; ? Array.from(children).flatMap(toTree)&nbsp; &nbsp;// 2&nbsp; &nbsp; : [ { element: dataset.element&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 3&nbsp; &nbsp; &nbsp; &nbsp; , children: Array.from(children).flatMap(toTree)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; ]console.log(toTree(root)[0])<div data-element="from-here">&nbsp; <div>skip me</div>&nbsp; <div>&nbsp; &nbsp; <div data-element="awesome">awesome text</div>&nbsp; &nbsp; <div data-element="collect-me">&nbsp; &nbsp; &nbsp; awesome text&nbsp; &nbsp; &nbsp; <div data-element="also-me">&nbsp; &nbsp; &nbsp; &nbsp; other text&nbsp; &nbsp; &nbsp; &nbsp; <div class="but-not-me">...</div>&nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; </div>&nbsp; </div></div>请注意,上面我们没有使用querySelectorAll,因为不需要额外的文档查询。然而,一些明显的改进是 -将树的形状定义为单独的函数,branch为重复任务定义一个助手,allToTreeconst branch = (element = "", children = []) =>&nbsp; &nbsp; // 1&nbsp; ({ element, children })const allToTree = (nodes = []) =>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 2&nbsp; Array.from(nodes).flatMap(toTree)toTree现在已经摆脱了复杂性。我们的意图很明确,每个功能都易于编写、测试和维护 -const toTree = ({ dataset = {}, children = [] }) =>&nbsp; dataset.element === undefined&nbsp; &nbsp; ? allToTree(children)&nbsp; &nbsp; : [ branch(dataset.element, allToTree(children) ]

鸿蒙传说

您可以使用Array.filter()它来过滤掉没有属性的元素data-element。但为了包含包装器 div,您可能data-element还需要添加属性,如下所示。const root = document.querySelector('[data-element="from-here"]');function traverse(node) {&nbsp; if (node.dataset.element) {&nbsp; &nbsp; return {&nbsp; &nbsp; &nbsp; element: node.dataset.element,&nbsp; &nbsp; &nbsp; children: Array.from(node.querySelectorAll(':scope > div'))&nbsp; &nbsp; &nbsp; &nbsp; .filter(child => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return child.dataset.element || Array.from(child.children).some(grandChild => grandChild.dataset.element)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// If element has dataset&nbsp; &nbsp; &nbsp;or&nbsp; The child has some children with dataset attribute&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; .map(childNode => traverse(childNode)),&nbsp; &nbsp; };&nbsp; } else if (Array.from(node.children).some(child => child.dataset.element)) {&nbsp; &nbsp; return Array.from(node.querySelectorAll(':scope > div')).filter(child => child.dataset.element).map(childNode => traverse(childNode))&nbsp; }}console.log(traverse(root));<div data-element="from-here">&nbsp; <div>skip me</div>&nbsp; <div>&nbsp; &nbsp; <div data-element="awesome">awesome text</div>&nbsp; &nbsp; <div data-element="collect-me">&nbsp; &nbsp; &nbsp; awesome text&nbsp; &nbsp; &nbsp; <div data-element="also-me">&nbsp; &nbsp; &nbsp; &nbsp; other text&nbsp; &nbsp; &nbsp; &nbsp; <div class="but-not-me">...</div>&nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; </div>&nbsp; </div></div>

呼啦一阵风

为什么不使用 usingnode.querySelectorAll(':scope [data-element]')来代替呢?这将覆盖具有该属性的所有元素,而不仅仅是第一层子元素。这是一个小提琴: https:&nbsp;//jsfiddle.net/cp21ykng/2/或者我错过了什么?
随时随地看视频慕课网APP

相关分类

Html5
我要回答