javascript 为什么jQuery或getElementById之类的DOM方法找不到元素?

exdqitrt  于 2023-03-28  发布在  Java
关注(0)|答案(6)|浏览(226)

document.getElementById$("#id")或任何其他DOM方法/ jQuery选择器找不到元素的可能原因是什么?
示例问题包括:

  • jQuery静默绑定事件处理程序失败
  • jQuery“getter”方法(.val().html().text())返回undefined
  • 返回null的标准DOM方法会导致以下任何错误:

未捕获的类型错误:无法设置null的属性“...”
未捕获的类型错误:无法设置null的属性(设置“...”)
未捕获的类型错误:无法读取null的属性“...”
未捕获的类型错误:无法读取null的属性(阅读“...”)
最常见的形式有:
未捕获的类型错误:无法将属性“onclick”设置为null
未捕获的类型错误:无法读取空值的属性“addEventListener
未捕获的类型错误:无法读取空值的属性“style

hrirmatl

hrirmatl1#

当脚本运行时,您试图查找的元素不在DOM中。
依赖DOM的脚本的位置会对其行为产生深远的影响。浏览器从上到下解析HTML文档。元素被添加到DOM,脚本(通常)在遇到它们时执行。**这意味着顺序很重要。**通常,脚本无法找到稍后出现在标记中的元素,因为这些元素尚未添加到DOM。
考虑以下标记;脚本#1无法找到<div>,而脚本#2成功:

<script>
  console.log("script #1:", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
  console.log("script #2:", document.getElementById("test")); // <div id="test" ...
</script>

那么,你该怎么做呢?你有几个选择:

选项1:移动您的脚本

鉴于我们在上面的例子中所看到的,一个直观的解决方案可能是简单地将脚本沿着标记向下移动,经过您想要访问的元素。事实上,很长一段时间以来,出于各种原因,将脚本放置在页面底部被认为是最佳实践。以这种方式组织,文档的其余部分将在执行脚本之前进行解析:

<body>
  <button id="test">click me</button>
  <script>
    document.getElementById("test").addEventListener("click", function() {
      console.log("clicked:", this);
    });
  </script>
</body><!-- closing body tag -->

虽然这是有意义的,并且是传统浏览器的可靠选择,但它是有限的,并且有更灵活的现代方法可用。

选项2:defer属性

虽然我们说过脚本是,“(通常)在遇到它们时执行”, 现代浏览器允许您指定不同的行为。如果您链接外部脚本,您可以使用defer属性。
[defer,布尔属性]被设置为向浏览器指示脚本将在文档被解析之后但在触发DOMContentLoaded之前执行。
这意味着您可以将标记为defer的脚本放置在任何地方,甚至是<head>,并且它应该可以访问完全实现的DOM。

<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>

只要记住...

  1. defer只能用于外部脚本,即:具有src属性的那些。
    1.注意browser support,即:IE〈10中的错误实现

选项3:模块

根据您的需求,您可以使用JavaScript模块。与标准脚本的其他重要区别(在此注明)中,模块会自动延迟,并且不限于外部源。
将脚本的type设置为module,例如:

<script type="module">
  document.getElementById("test").addEventListener("click", function(e) {
    console.log("clicked: ", this);
  });
</script>
<button id="test">click me</button>

选项四:延迟事件处理

将侦听器添加到在分析文档后触发的事件。

DOMContentLoaded事件

DOMContentLoaded在DOM从初始解析中完全构造完成后触发,而不等待样式表或图像等加载。

<script>
  document.addEventListener("DOMContentLoaded", function(e){
    document.getElementById("test").addEventListener("click", function(e) {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>

窗口:加载事件

load事件在DOMContentLoaded和其他资源(如样式表和图像)加载后触发。因此,它的触发时间比我们预期的要晚。不过,如果您考虑的是IE8等旧浏览器,则支持几乎是通用的。当然,您可能需要polyfill for addEventListener()

<script>
  window.addEventListener("load", function(e){
    document.getElementById("test").addEventListener("click", function(e) {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>

jQuery的ready()

jQuery的ready()提供了一个混合解决方案,尽可能使用DOMContentLoaded,必要时故障转移到window:load,如果DOM已经完成,则立即触发其回调。
你可以将你的ready handler作为$(*handler*)直接传递给jQuery,例如:

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script>
  $(function() {
    $("#test").click(function() {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>

选项五:活动委托

将事件处理委托给目标元素的祖先。
当元素引发事件时(假设它是一个冒泡事件,并且没有任何东西阻止它的传播),该元素祖先中的每个父元素,一直到window,这允许我们将一个处理程序附加到一个现有的元素上,并在事件从它的后代中冒泡时对事件进行采样......甚至是从附加处理程序后添加的后代中。我们所要做的就是检查事件,看看它是否是由所需的元素引发的,如果是,则运行我们的代码。
通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,请选择目标元素最近的可靠祖先,而不是将其附加到document

原生JavaScript

<div id="ancestor"><!-- nearest ancestor available to our script -->
  <script>
    document.getElementById("ancestor").addEventListener("click", function(e) {
      if (e.target.id === "descendant") {
        console.log("clicked:", e.target);
      }
    });
  </script>
  <button id="descendant">click me</button>
</div>

jQuery的on()

jQuery通过on()提供了此功能。给定事件名称,所需后代的选择器和事件处理程序,它将解析您的委托事件处理并管理您的this上下文:

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<div id="ancestor"><!-- nearest ancestor available to our script -->
  <script>
    $("#ancestor").on("click", "#descendant", function(e) {
      console.log("clicked:", this);
    });
  </script>
  <button id="descendant">click me</button>
</div>
axzmvihb

axzmvihb2#

**简短:**因为您要查找的元素在文档中不存在(尚未)。

元素可能不存在的原因有三个:
1.带有传递ID的元素在文档中实际上不存在。您应该仔细检查传递给getElementById的ID是否与(生成的)HTML中现有元素的ID匹配,并且您没有 * 拼错 * ID(ID * 区分大小写 *!)。
如果您使用的是getElementById,请确保only给出了元素的ID(例如,document.getElemntById("the-id"))。如果您正在使用接受CSS选择器的方法(如querySelector),请确保在ID之前包含#,以表明您正在查找ID(例如,document.querySelector("#the-id"))。您必须#getElementById一起使用,并且必须将其与querySelector和类似物一起使用。还要注意,如果ID中包含在CSS标识符中无效的字符(例如.;包含.字符的id属性是不好的做法,但有效),在使用querySelectordocument.querySelector("#the\\.id"))时必须转义这些属性,但在使用getElementByIddocument.getElementById("the.id"))时不必转义。
1.元素在您调用getElementById时不存在。
1.即使您可以在页面上看到该元素,它也不在您正在查询的文档中,因为它位于iframe(它是自己的文档)中。当您搜索包含iframes中的元素的文档时,不会搜索它们。
如果问题是原因3(它在iframe或类似的文件中),您需要查看iframe中的文档,而不是父文档,可能是通过获取iframe元素并使用其contentDocument属性来访问其文档(仅限同源)。
第二个原因-它还没有出现-是很常见的。浏览器从上到下解析和处理HTML。这意味着在DOM元素出现在HTML中之前发生的任何对DOM元素的调用都将失败。
请看下面的例子:

<script>
    var element = document.getElementById('my_element');
</script>

<div id="my_element"></div>

div出现在script之后。在脚本执行时,该元素还不存在,getElementById将返回null

jQuery软件

这同样适用于jQuery中的所有选择器。如果你 * 拼错了 * 你的选择器,或者你试图 * 在它们实际存在之前 * 选择它们,jQuery将无法找到它们。
另一种情况是,由于您加载了没有协议的脚本并且从文件系统运行,因此找不到jQuery:

<script src="//somecdn.somewhere.com/jquery.min.js"></script>

此语法用于允许脚本通过HTTPS加载到协议为https://的页面上,并在协议为http://的页面上加载HTTP版本
它具有尝试加载file://somecdn.somewhere.com...但失败的副作用

解决方案

在调用getElementById(或任何DOM方法)之前,请确保要访问的元素存在,即DOM已加载。
这可以通过简单地将JavaScript * 放在 * 相应的DOM元素之后来确保

<div id="my_element"></div>

<script>
    var element = document.getElementById('my_element');
</script>

在这种情况下,您也可以将代码放在结束body标记(</body>)之前(所有DOM元素在脚本执行时都可用)。
其他解决方案包括监听load[MDN]DOMContentLoaded[MDN]事件。在这些情况下,将JavaScript代码放在文档中的哪个位置并不重要,您只需记住将所有DOM处理代码放在事件处理程序中。
示例:

window.onload = function() {
    // process DOM elements here
};

// or

// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
    // process DOM elements here
});

有关事件处理和浏览器差异的更多信息,请参阅articles at quirksmode.org

jQuery软件

首先确保jQuery正确加载。Use the browser's developer tools查看是否找到jQuery文件,如果没有,则更正URL(例如,在开头添加http:https:方案,调整路径等)。
监听load/DOMContentLoaded事件正是jQuery对.ready()[docs]所做的。所有影响DOM元素的jQuery代码都应该在该事件处理程序中。
事实上,jQuery tutorial明确声明:
由于我们在使用jQuery时所做的几乎所有事情都是读取或操作文档对象模型(DOM),因此我们需要确保一旦DOM准备就绪,我们就开始添加事件等。
为此,我们为文档注册一个就绪事件。

$(document).ready(function() {
// do stuff when DOM is ready
});

或者,您也可以使用简写语法:

$(function() {
    // do stuff when DOM is ready
});

两者等同。

hfwmuf9z

hfwmuf9z3#

基于id的选择器不起作用的原因

1.指定id的元素/DOM尚不存在。
1.该元素存在,但未在DOM中注册[在从 AJAX 响应动态追加HTML节点的情况下]。
1.存在多个具有相同id的元素,这会导致冲突。

解决方案

1.尝试在元素声明之后访问它,或者使用类似$(document).ready();的东西
1.对于来自 AJAX 响应的元素,使用jQuery的.bind()方法。旧版本的jQuery有.live()方法。
1.使用工具[例如,用于浏览器的webdeveloper插件]查找重复的ID并删除它们。

jpfvwuh4

jpfvwuh44#

如果您试图访问的元素位于iframe内部,并且您试图在iframe的上下文之外访问它,这也会导致它失败。
如果你想在iframe中获取一个元素,你可以找到here

t30tvxxf

t30tvxxf5#

正如@FelixKling所指出的,最有可能的情况是您正在寻找的节点不存在(尚未)。
然而,现代开发实践通常可以使用DocumentFragments或简单地直接分离/重新附加当前元素来操作文档树之外的文档元素。这些技术可以用作JavaScript模板的一部分,或者在元素被大量更改时避免过多的重绘/回流操作。
类似地,在现代浏览器中推出的新的“Shadow DOM”功能允许元素成为文档的一部分,但不能通过document.getElementById及其所有兄弟方法(querySelector等)进行查询。这样做是为了封装功能并专门隐藏它。
同样,尽管如此,最有可能的情况是,您正在寻找的元素根本不在文档中,您应该按照Felix的建议去做。然而,您也应该意识到,这越来越不是元素可能无法找到的唯一原因(无论是暂时的还是永久的)。

xytpbqjk

xytpbqjk6#

如果脚本执行顺序不是问题,则问题的另一个可能原因是未正确选择元素:

  • getElementById要求传入的字符串必须是ID verbatim,而没有其他任何内容。如果您在传入的字符串前面加上#,并且ID不是以#开头,则不会选择任何内容:
<div id="foo"></div>
// Error, selected element will be null:
  document.getElementById('#foo')
  // Fix:
  document.getElementById('foo')
  • 类似地,对于getElementsByClassName,不要在传入的字符串前面加上.

一个二个一个一个

  • 使用querySelector、querySelectorAll和jQuery,要匹配具有特定类名的元素,请将.直接放在类之前。类似地,要匹配具有特定ID的元素,请将#直接放在ID之前:
<div class="baz"></div>
// Error, selected element will be null:
  document.querySelector('baz')
  $('baz')
  // Fix:
  document.querySelector('.baz')
  $('.baz')

在大多数情况下,这里的规则与CSS选择器的规则相同,可以在here中详细查看。

  • 要匹配具有两个或更多属性的元素(如两个类名,或一个类名和一个data-属性),请将每个属性的选择器放在选择器字符串中彼此相邻,* 不使用 * 空格分隔它们(因为空格表示descendant selector)。例如,要选择:
<div class="foo bar"></div>

使用查询字符串.foo.bar。要选择

<div class="foo" data-bar="someData"></div>

使用查询字符串.foo[data-bar="someData"]。要选择下面的<span>

<div class="parent">
    <span data-username="bob"></span>
  </div>

使用div.parent > span[data-username="bob"]

  • 大写和拼写对以上所有内容都很重要。如果大写不同,或者拼写不同,则不会选择该元素:
<div class="result"></div>
// Error, selected element will be null:
  document.querySelector('.results')
  $('.Result')
  // Fix:
  document.querySelector('.result')
  $('.result')
  • 您还需要确保这些方法的大小写和拼写正确。请使用以下方法之一:
$(selector)
document.querySelector
document.querySelectorAll
document.getElementsByClassName
document.getElementsByTagName
document.getElementById

任何其他拼写或大写都不起作用。例如,document.getElementByClassName将抛出错误。

  • 确保你给这些选择器方法传递了一个字符串。如果你给querySelectorgetElementById等传递了一些不是字符串的东西,它几乎肯定不会工作。
  • 如果要选择的元素上的HTML属性用引号括起来,则它们必须是纯直引号(单引号或双引号);如果您尝试按ID、类或属性进行选择,则等花引号将不起作用。

相关问题