为什么当我在遍历 DOM 时使用节点列表或 HTML 集合中的元素时,我会变得未定义?

Why do I get undefined when I use elements from a node-list or HTML-Collection when traversing the DOM?

提问人:William S 提问时间:7/4/2022 最后编辑:Shachar297William S 更新时间:7/4/2022 访问量:475

问:

在遍历 DOM 时,有时会在一个父元素下有许多子元素。

我可以使用 a 将这些子项作为节点列表获取,也可以使用 .querySelectorAllelement.children

但是当我选择一个用于放入我的 DOM 树遍历时,我得到 .undefined

我尝试将节点列表和HTML集合转换为数组并以这种方式使用它们,但没有成功。我尝试使用常规for循环和foreach循环来选择单个元素,但总是失败。undefined

我创建了一个虚构的情况来显示这种情况,尽管问题无需遍历 DOM 即可解决,但您必须想象这是必要的。

  • [HTML全文]
<!DOCTYPE html>
<html lang="en">
   <head>
      <title>Testing</title>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link rel="stylesheet" href="eric_meyer_reset_2.css" />
      <link rel="stylesheet" href="styles.css" />
   </head>
   <body>
      <div id="wrapper">
         <div id="topOfPage">
            <header>
               <h1>Random Number Generator</h1>
            </header>
            <div id="simulated RNG">
               <p>Your number is: <span id="theRandomNumber">2</span></p>
               <button type="button" id="prizeOpenButton">Press for Prize</button>
            </div>
         </div>
         <div id="bottomOfPage">
            <!-- These sections are added dynamically during the script operation -->
            <section class="joy" data-id="0">Hurray</section>
            <section class="joy" data-id="1">Hurrah</section>
            <section class="joy" data-id="2">Yay</section>
            <section class="joy" data-id="3">Congrats</section>
         </div>
      </div>
      <script src="main.js"></script>
   </body>
</html>
  • CSS的
.joy {
   display: none
}
  • JavaScript的
const randomNumber = document.getElementById("theRandomNumber");
const prizeButton = document.getElementById("prizeOpenButton")

let matchNumberToPrizeSection = function (event) {
   let prizeSections = document.querySelectorAll(".joy");

   let theSection = null;

   for (let i = 0; i < prizeSections.length; i++) {
      if (prizeSections[i].dataset.id === randomNumber) {
         theSection = prizeSections[i];
      }
   }

   // lets say for some reason we have to traverse the DOM
   // starting with the button going to the "theSection" above
   let winningSection = event.target.parentElement.parentElement.nextElementSibling.theSection;

   console.log(winningSection.innerHTML);
}
prizeButton.addEventListener("click", matchNumberToPrizeSection);
JavaScript HTML DOM 遍历

评论

0赞 PsiKai 7/4/2022
看起来从未定义过,这是完整的代码吗?winningSection
0赞 William S 7/4/2022
我会回过头来定义它,但它仍然不会改变结果。
0赞 PsiKai 7/4/2022
没关系,我只是想在我尝试回答之前消除错别字的可能性。
0赞 Coffee bean 7/4/2022
可能是因为您尝试在元素被动态添加到 DOM 之前将其作为目标。 尝试在声明后控制台注销 prizeSections,我想它会是空的,基本上是一个空的 NodeList
0赞 William S 7/4/2022
感谢您发现我的代码存在另一个问题。但是,在元素被添加到 DOM 之前,我不会针对它们。 声明 prizeSections 不会对问题产生任何影响。但是,我将纠正我的错误并确保这一事实。

答:

0赞 PsiKai 7/4/2022 #1

我看到了许多问题,但以下代码有效。

首先,您正在比较元素,而不是元素的文本或 data-id。我仔细检查并清理了那里的不一致之处。

  1. 这是返回一个元素
const randomNumber = document.getElementById("theRandomNumber");
  • 但是您正在将其与数字进行比较.他们永远不会匹配dataset.id
if (prizeSections[i].dataset.id === randomNumber)
let winningSection = event.target.parentElement.parentElement.nextElementSibling.theSection
  • 该变量指向一个元素,而该元素不是 的属性。我想进行元素链接并搜索作为 的子元素,然后调用它并传入 id 或类或其他任何内容theSectionnextElementSiblingnextElementSiblingquerySelector
let winningSection = event.target.parentElement.parentElement.nextElementSibling.querySelector([`data-id="${theSection.dataset.id}"`])

在此示例中,上述内容是多余的,我认为没有理由像您那样遍历 DOM。这是创建 bug 的简单方法,尤其是在您四处移动或向标记添加项目时。

由于您拥有 ,因此可以查询整个文档以查找该 ID。这将在将来对 DOM 的更改中得到强化。data-id

const randomNumber = document.getElementById("theRandomNumber").innerText;
const prizeButton = document.getElementById("prizeOpenButton")

let matchNumberToPrizeSection = function (event) {
   let prizeSections = document.querySelectorAll(".joy");

   let theSection = null;

   for (let i = 0; i < prizeSections.length; i++) {
      if (prizeSections[i].dataset.id === randomNumber) {
         theSection = prizeSections[i].dataset.id;
      }
   }

   let winningSection = document.querySelector(`[data-id="${theSection}"]`);

   console.log(winningSection.innerHTML);
}
prizeButton.addEventListener("click", matchNumberToPrizeSection);
.joy {
   display: none
}
      <div id="wrapper">
         <div id="topOfPage">
            <header>
               <h1>Random Number Generator</h1>
            </header>
            <div id="simulated RNG">
               <p>Your number is: <span id="theRandomNumber">2</span></p>
               <button type="button" id="prizeOpenButton">Press for Prize</button>
            </div>
         </div>
         <div id="bottomOfPage">
            <!-- These sections are added dynamically during the script operation -->
            <section class="joy" data-id="0">Hurray</section>
            <section class="joy" data-id="1">Hurrah</section>
            <section class="joy" data-id="2">Yay</section>
            <section class="joy" data-id="3">Congrats</section>
         </div>
      </div>

评论

0赞 William S 7/4/2022
这样做的全部原因是想象必须遍历 DOM 并解决我的问题。这只是一个虚构的问题。我可以给你我的真实代码,但它很长。问题是每次我尝试遍历 DOM 并且必须使用节点列表或 HTML 集合中的元素时,我都会得到未定义。
0赞 PsiKai 7/4/2022
好的,这很好,无论如何,你遍历 DOM 似乎与你变得未定义的原因分开。我提到了一些不一致之处。我将更新我的答案以专门指出它们。
0赞 Bergi 7/4/2022
目前尚不清楚您为什么更改为 ,它只是将 分配给 。当然,可以只用 [data-id=“${randomNumber}”] 替换整个循环,但我认为这不是 OP 的重点。theSection = prizeSections[i];theSection = prizeSections[i].dataset.id;randomNumbertheSectionconst theSection = document.querySelector();
0赞 PsiKai 7/4/2022
@Bergi在这个代码示例中有很多可以重构的地方,但就我而言,OP 遇到的问题不是基于节点列表或基于 HTML 集合的。我进行了更新以提高清晰度。
0赞 William S 7/4/2022
再次感谢PsiKai。如果你自己在一个更好的例子中尝试,那就没问题了。我坚持这样一个事实,即在尝试遍历 DOM 时,您无法使用节点列表或 HTML 集合中的元素。这是我的问题,我为事实证明是一个糟糕的例子问题道歉。
0赞 William S 7/4/2022 #2

虽然在构建 DOM 遍历时不能直接或间接使用 node-list 或 HTML-Collection 元素。我最终发现,我可以在许多子元素之前使用该元素,在我最初的问题中,该元素的 id 为 bottomOfPage。我所要做的是使用 node-list 元素找到我需要的子元素的索引,并在父元素中使用该索引,如下所示。divdivevent.target.parentElement.parentElement.nextElementSibling.children(index)