【问题标题】:JS DOM issue: Uncaught TypeError: Cannot read property 'addEventListener' of null becomes Uncaught Reference ErrorJS DOM 问题:Uncaught TypeError: Cannot read property 'addEventListener' of null 变成 Uncaught Reference Error
【发布时间】:2021-08-08 17:12:08
【问题描述】:

这是我从这里的教程中下载的代码:(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 人能帮我摆脱困境吗?

【问题讨论】:

  • 我想我会补充说错误发生在这里: nextBtn.forEach((button) => { button.addEventListener("click", () => { changeStep("next");

标签: javascript html jquery


【解决方案1】:

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 树中确实拥有您要查询的所有元素吗?

【讨论】:

  • 谢谢,我理解你的建议,但忘了说我已经包装了整个 JS 是一个事件监听器,这只是把罐子踢到了路上,新的错误是“未捕获的 ReferenceError:nextBtn未在 multistep.js:24 处定义”(nextBtn.forEach((button),,,,,etc...的代码)。这就是为什么我将每个语句包装在它自己的事件侦听器中。按照你的建议做只是带回了那个错误. 可能我收到的第一个错误是由于根本没有 body 标签,因为它不在我开始使用的原始代码中。同样的错误,不同的代码行。
  • 我发现了我刚刚发布的问题 - 我的语法错误(括号放置不当)。我是新手,所以非常感谢您的帮助!那么, 标签在 html 中是“必需的”吗?最后,在 JS 中如何定义函数的顺序是否重要?例如“function a() { call function b; }; function b(){}; 感谢您的提示,非常感谢!
  • &lt;body&gt; 标签可能是标准所要求的,但如果您不包含它,您的页面仍会加载。浏览器只会自动添加它。除非您执行以下两项操作,否则函数定义的顺序无关紧要:1)通过将函数分配给变量来定义函数,例如let f = function() {} AND 2)调用一个函数,该函数调用另一个尚未调用的函数在调用它的那一刻被分配给变量(你用来调用它的名字)。如果您想了解更多信息,只需 google Javascript Hoisting。
  • "Javascript Hoisting",将检查一下,感谢您的建议。您的提示帮助我解决了这个问题,谢谢!
猜你喜欢
  • 1970-01-01
  • 2018-06-30
  • 2019-08-08
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 2021-07-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多