首页 > 解决方案 > 试图重新创建 getElementsByClassName

问题描述

function getElementsByClassName(cls) {
  const result = [];
  const checkClass = (element) => {
    const children = element.children;
    for (let i = 0; i < children.length; i++) {
      if()
        if (children[i].contains(cls)) {
          result.push(children[i]); 
        }
      if (children[i].hasChildNodes()) {
     
        checkClass(children[i]);      }
    }
  };
  checkClass(document.body); 
  return result;
}

大家好。

据我了解, childNodes 具有无法通过 .contain 方法的未定义节点,例如“文本”。

所以我从 childNodes 切换到 children,然后我仍然收到与下面相同的错误消息。

getElementsByClassName("targetClassName")

VM1349:8 Uncaught TypeError: Failed to execute 'contains' on 'Node': parameter 1 is not of type 'Node'.
    at checkClass (<anonymous>:8:23)
    at getElementsByClassName (<anonymous>:18:3)
    at <anonymous>:1:1

我知道问题与遍历节点列表然后尝试应用包含有关,但我只是不明白为什么它在切换到元素而不是节点后不会执行它。

请指教。

标签: javascriptdom

解决方案


据我了解, childNodes 具有无法通过 .contain 方法的未定义节点,例如“文本”。

我不知道“未定义的节点”是什么意思,但文本节点是完全有效的参数contains

const div = document.getElementById("x");
const text = div.firstChild;
console.log(text.nodeName);      // #text
console.log(div.contains(text)); // true
<div id="x">foo</div>

也就是说,children在你的函数中使用是合理的,因为只有元素可以有类,你的代码不需要处理文档片段(例如,检查它们的内容),所以你只需要查看元素,而不是其他类型的节点.

我假设它是这个代码:

if (children[i].contains(cls)) {

这给了你

参数 1 不是“节点”类型

cls在您的代码中是一个字符串,而不是一个节点。正如错误所说,contains接受Nodes。它不接受字符串。


一些旁注:

  1. 在你自己的代码中正确地重新创建getElementsByClassName是相当复杂的,因为它返回一个live ,而不是像这样HTMLCollection的快照。这意味着正确地重新创建它需要使用 a来跟踪随时间的变化并随着事情的变化更新您返回的列表。但是要在没有“实时”功能的情况下重新创建它,您可能需要一个递归函数。NodeListquerySelectorAllMutationObserver

  2. getElementsByClassName在以空格分隔的字符串中接受多个类名,而不仅仅是一个。

  3. 它适用于整个文档,而不仅仅是body(中的元素head可以有类)。

FWIW,非实时解决方案的一般形式可能如下所示:

function getElementsByClassName(cls) {
    const classes = cls.split(" ");
    return worker(document.documentElement, classes, []);
}

function worker(element, classes, result) {
    if (/*element has all the classes*/) {
        result.push(element);
    }
    for /*...loop through `children`...*/ {
        worker(child, classes, result);
    }
    return result;
}

或者,正如Peter Seliger 指出的那样,您可以HTMLCollection从 from开始getElementsByTagName并过滤它。我认为这是一个学习练习,所以这取决于学习练习的目的。


推荐阅读