Javascript HTML .children遍历

Javascript HTML .children traversal

我正在尝试浏览文档中的所有元素并提取具有目标 class 名称的元素。重要的是,我需要在不使用 document.getElementsByClassName(className) / document.querySelectorAll 等的情况下完成它——这就是本次学习练习的重点。

这是javascript:

var getElementsByClassName = function(className){
  var rootElem = document.body;
  var collectionResult = [];
  if (rootElem.getAttribute("class") == className) {
    collectionResult.push(rootElem);
  };
  var nextTier = function(collectionResult, rootElem) {
    var thisTier = rootElem.children;
    for (i=0; i<thisTier.length; i++) {
      var classes = thisTier[i].getAttribute("class");
      if (classes != undefined && classes.includes(className)) {
        collectionResult.push(thisTier[i]);
      };
      var childrenArray = thisTier[i].children; 
      if (childrenArray.length > 0) {
        nextTier(collectionresult, childrenArray)
      };
    };
  };
  nextTier(collectionResult, rootElem);
  return collectionResult;
};

这是 HTML 结构中我遇到问题的部分:

<p>
  <div class="somediv">
    <div class="innerdiv">
      <span class="targetClassName">yay</span>
    </div>
   </div>
 </p>

该代码适用于具有任意数量的非嵌套元素的页面的其余部分。但是一旦 var childrenArray = thisTier[i].children 到达 div.somediv 元素,它就会有 childrenArray == undefined 而不是拉动 div.innerdiv 元素。

我是不是误解了 element.children 的工作原理?

Array.prototype.flatMap 是将树(如 DOM)展平为值数组(如元素列表)的有效工具 -

function getElementsByClassName (node, query) {   
  function matchAll (children) {
    return Array
      .from(children)
      .flatMap(c => getElementsByClassName(c, query))
  }
  if (node.classList && node.classList.contains(query))
    return [ node, ...matchAll(node.childNodes) ]
  else
    return matchAll(node.childNodes)
}

const result =
  getElementsByClassName(document, "targetClassName")
  
console.log(result)

// [ <div class="somediv targetClassName">…</div>
// , <span class="targetClassName">yay1</span>
// , <span class="targetClassName">yay2</span>
// , <span class="targetClassName">yay3</span>
// ]
<div class="somediv targetClassName">
  <div class="innerdiv">
    <span class="targetClassName">yay1</span>
  </div>
</div>
<div class="somediv">
  <div class="innerdiv">
    <span class="targetClassName">yay2</span>
  </div>
</div>
<div class="somediv">
  <div class="innerdiv">
    <span class="targetClassName">yay3</span>
  </div>
</div>

你似乎把事情复杂化了。

function getElementsByClassName(className, root) {
  if(!root) root = document.documentElement;
  return [].reduce.call(root.children, function(arr, child) {
    if(child.classList.contains(className)) arr.push(child);
    return arr.concat(getElementsByClassName(className, child))
  }, []);
}

function getElementsByClassName(className, root) {
  if(!root) root = document.documentElement;
  return [].reduce.call(root.children, function(arr, child) {
    if(child.classList.contains(className)) arr.push(child);
    return arr.concat(getElementsByClassName(className, child))
  }, []);
}
console.log(getElementsByClassName("targetClassName"));
<div class="somediv targetClassName">
  <div class="innerdiv">
    <span class="targetClassName">yay1</span>
  </div>
</div>
<div class="somediv targetClassName">
  <div class="innerdiv targetClassName">
    <span class="targetClassName">yay2</span>
  </div>
</div>
<div class="somediv">
  <div class="innerdiv">
    <span class="targetClassName">yay3</span>
  </div>
</div>