首页 > 解决方案 > JS DOM 问题:未捕获类型错误:无法读取 null 的属性“addEventListener”变为未捕获引用错误

问题描述

这是我从这里的教程中下载的代码:( https://codepen.io/Web_Cifar/pen/PoNNEYY ) 希望将其适应我正在做的事情。我把它拼凑在一起,虽然它在演示中有效(并且有一个很好的 YT 视频,他通过它),但在现场情况下它对我不起作用。我正在尝试构建一个类似于 Gravity Forms 的多页数据输入表单(GF 有一些对我不起作用的怪癖)。

我得到的第一个 Javascript 错误是:“未捕获的 TypeError:无法读取属性 'addEventListener' of null”。在 StackO 上进行研究后,我得到的一般建议是,可能尚未为我的文档加载 DOM,而在此之前我们正在调用 JS,两个建议似乎很受欢迎:1)。将您的 JS 文件移动到 HTML 文档的底部,就在您的 body 标签关闭之前(它最初在我的标签中......移动它没有任何改变)。2. 将您的 JS 函数包装在以下代码中:"window.addEventListener('DOMContentLoaded', (event) => {" 以在调用函数之前强制加载 DOM。

执行 #2 将错误更改为:ReferenceError: "changeStep" is not defined。JS 代码中有一个名为“changeStep”的函数。如果你参考上面的教程,你会发现它最初是在 JS 文件中定义的最后一个函数,所以我认为将它移到顶部会改变它。没有骰子。我已经做了一些检查,看看一些简单的 JQuery 测试是否在我的环境中工作并且它们确实有效。我对 JS 比较陌生,但我不明白为什么 DOM 不会被加载,也不明白为什么不能引用有问题的函数。

这是我的 HTML 代码的骨架:

<html>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script> /* A recommended test to see if jQuery is working, and it is. */
$(document).ready(function(){
    $("button").click(function(){
        alert("jQuery is working perfectly.");
    });      
});
</script>
<link rel="stylesheet" href="/Users/Me/Documents/multistep.css">
<body>
etc.....(moving to end of html file....)

<script type = "text/javascript" src = "/Users/Me/Documents/multistep.js" ></script>
</body>
</html>

这是我的整个 JS 脚本:

const steps = Array.from(document.querySelectorAll("form .step"));
const nextBtn = document.querySelectorAll("form .next-btn");
const prevBtn = document.querySelectorAll("form .previous-btn");
const form = document.querySelector("form");

window.addEventListener('DOMContentLoaded', (event) => {
    function changeStep(btn) {
      let index = 0;
      const active = document.querySelector(".active");
      index = steps.indexOf(active);
      steps[index].classList.remove("active");
      if (btn === "next") {
        index++;
      } else if (btn === "prev") {
        index--;
      }
      steps[index].classList.add("active");
    }
    });

window.addEventListener('DOMContentLoaded', (event) => {    
nextBtn.forEach((button) => {
  button.addEventListener("click", () => {
    changeStep("next");
  });
});
});

window.addEventListener('DOMContentLoaded', (event) => {    
prevBtn.forEach((button) => {
  button.addEventListener("click", () => {
    changeStep("prev");
  });
});
});

window.addEventListener('DOMContentLoaded', (event) => {
    form.addEventListener("submit", (e) => {
        e.preventDefault();
        const inputs = [];
        form.querySelectorAll("input").forEach((input) => {
          const { name, value } = input;
          inputs.push({ name, value });
        });
        console.log(inputs);
        form.reset();
      });
});

请注意,我基本上将所有内容都包装在“window.addEventListener ...”标签中(根据上面的 Google 建议),我从引用的演示(在 codePen 中工作)中所做的唯一其他更改是将 changeStep 函数移动到文件中的第一个函数。

我不知道发生了什么。有经验的 JS 人能帮我摆脱困境吗?

标签: javascripthtmljquery

解决方案


changeStep是在第一个事件监听器的匿名回调函数中定义的,所以它只在那个函数中可见。如果要在该函数范围之外访问它,则必须在函数之外定义它,即在调用addEventListener. 但更好的解决方案是将所有内容放在一个事件侦听器中。我看不出为什么必须要有三个。

此外,由于您仍然querySelector在事件侦听器之外调用,它并没有解决原来的问题。这正是您在加载 DOM 上下文后想要做的事情,因此您也必须将其放入事件侦听器中。

所以解决方案是基本上把所有东西都放在一个事件监听器中:

window.addEventListener('DOMContentLoaded', (event) => {
  const steps = Array.from(document.querySelectorAll("form .step"));
  const nextBtn = document.querySelectorAll("form .next-btn");
  const prevBtn = document.querySelectorAll("form .previous-btn");
  const form = document.querySelector("form");

  function changeStep(btn) {
    let index = 0;
    const active = document.querySelector(".active");
    index = steps.indexOf(active);
    steps[index].classList.remove("active");
    if (btn === "next") {
      index++;
    } else if (btn === "prev") {
      index--;
    }
    steps[index].classList.add("active");
  }

  nextBtn.forEach((button) => {
    button.addEventListener("click", () => {
      changeStep("next");
    });
  });

  prevBtn.forEach((button) => {
    button.addEventListener("click", () => {
      changeStep("prev");
    });
  });
});

这应该回答您关于为什么会出现第二个错误的问题,但实际上将脚本放在 body 标记的末尾应该首先解决了问题,所以第一个错误可能有不同的原因。您确定您的 script 标签位于 body 标签的最后,并且您确实在 DOM 树中拥有所有要查询的元素吗?


推荐阅读