为什么 jQuery 或 DOM 方法(如 getElementById)找不到元素?

Why does jQuery or a DOM method such as getElementById not find the element?

提问人:Felix Kling 提问时间:12/25/2012 最后编辑:IvarFelix Kling 更新时间:9/21/2023 访问量:195761

问:

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

示例问题包括:

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

未捕获的 TypeError:无法设置属性“...”of null 未捕获的 TypeError:无法设置 null 的属性(设置 '...')

未捕获的 TypeError:无法读取属性“...”of null
未捕获的 TypeError:无法读取 null 的属性(读取“...”)

最常见的形式是:

未捕获的 TypeError:无法将属性“onclick”设置为 null 未捕获的 TypeError:无法读取 null 的属性“addEventListener” 未捕获的 TypeError:无法读取 null

的属性“style”

javascript jquery dom

评论

43赞 Felix Kling 12/25/2012
关于为什么找不到某个 DOM 元素,人们会问很多问题,原因通常是因为 JavaScript 代码放在 DOM 元素之前。这旨在成为此类问题的规范答案。这是社区维基,所以请随时改进它

答:

616赞 canon 1/4/2012 #1

脚本运行时,您尝试查找的元素不在 DOM 中。

依赖 DOM 的脚本的位置会对其行为产生深远的影响。浏览器从上到下解析 HTML 文档。元素被添加到 DOM 中,脚本(默认情况下)在遇到它们时执行。这意味着顺序很重要。通常,脚本找不到标记中稍后出现的元素,因为这些元素尚未添加到 DOM 中。

请考虑以下标记;脚本 #1 找不到 while 脚本 #2 成功:<div>

<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 之前执行。

这意味着你可以放置一个标记有 任何地方的脚本,甚至是 ,并且它应该可以访问完全实现的 DOM。defer<head>

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

请记住......

  1. defer只能用于外部脚本,即:具有属性的脚本。src
  2. 注意浏览器支持,即:IE < 10 中的错误实现

选项 3:模块

根据您的要求,您可以使用 JavaScript 模块。与标准脚本的其他重要区别(在此处注明)中,模块是自动延迟的,不限于外部源。

将脚本设置为 ,例如:typemodule

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


选项 4:延迟事件处理

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

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 事件

load 事件在加载完其他资源(如样式表和图像)后触发。出于这个原因,它的触发时间比我们预期的要晚。不过,如果您正在考虑像 IE8 这样的旧浏览器,那么这种支持几乎是普遍的。当然,您可能需要 addEventListener() 的 polyfillDOMContentLoaded

<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()

DOMContentLoaded每个人都有自己的警告。jQuery 的 ready() 提供了一个混合解决方案,在可能的情况下使用,在必要时故障转移到,并在 DOM 已经完成时立即触发其回调。window:loadDOMContentLoadedwindow:load

您可以将准备好的处理程序直接传递给 jQuery,例如:$(handler)

<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>


选项 5:事件委派

将事件处理委托给目标元素的上级。

当一个元素引发一个事件时(前提是它是一个冒泡事件,并且没有任何东西阻止它的传播),该元素祖先中的每个父元素(一直到 )也会接收该事件。这允许我们将处理程序附加到现有元素,并在事件从其后代冒出时对其进行采样......甚至来自附加处理程序后添加的后代。我们所要做的就是检查事件,看看它是否是由所需的元素引发的,如果是,则运行我们的代码。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>

评论

3赞 lStoilov 2/8/2022
这个属性对我有很大帮助。此外,这可能是我在这里见过的最详细的答案之一。defer
169赞 12 revs, 4 users 83%Felix Kling #2

简短明了:因为您要查找的元素在文档中尚不存在。


对于本答案的其余部分,我将使用 getElementById 作为示例,但这同样适用于 getElementsByTagNamequerySelector 和任何其他选择元素的 DOM 方法。

可能的原因

元素可能不存在的原因有三个:

  1. 具有传递 ID 的元素实际上并不存在在文档中。您应该仔细检查您传递的 ID 是否与(生成的)HTML 中现有元素的 ID 真正匹配,并且您没有拼错 ID(ID 区分大小写getElementById

    如果您使用的是 ,请确保提供元素的 ID(例如,)。如果您使用接受 CSS 选择器的方法(例如),请确保在 ID 之前包含 ,以指示您正在寻找 ID(例如,)。不得将 with 一起使用,并且必须将其与 和 similar 一起使用。另请注意,如果 ID 中包含在 CSS 标识符中无效的字符(例如 ; 包含字符的属性是不好的做法,但有效),在使用 ()) 时必须转义这些属性,但在使用 () 时则不然。getElementByIddocument.getElemntById("the-id")querySelector#document.querySelector("#the-id")#getElementByIdquerySelector.id.querySelectordocument.querySelector("#the\\.id")getElementByIddocument.getElementById("the.id")

  2. 调用 时,该元素不存在。getElementById

  3. 该元素不在你正在查询的文档中,即使你可以在页面上看到它,因为它位于一个(这是它自己的文档)中。当您搜索包含元素的文档时,不会搜索这些元素。iframeiframes

如果问题是原因 3(它在 an 或类似中),则需要在 而不是父文档中查看文档,也许可以通过获取元素并使用其 contentDocument 属性访问其文档(仅限同源)。这个答案的其余部分解决了前两个原因。iframeiframeiframe

第二个原因——它还没有出现——很常见。浏览器从上到下解析和处理 HTML。这意味着在 DOM 元素出现在 HTML 中之前发生的对 DOM 元素的任何调用都将失败。

请看以下示例:

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

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

出现在 之后。在执行脚本时,该元素不存在,将返回 。divscriptgetElementByIdnull

jQuery的

这同样适用于jQuery的所有选择器。如果你拼错了你的选择器,或者你试图在元素实际存在之前选择它们,jQuery将无法找到它们。

一个额外的转折点是,当找不到jQuery时,因为你加载了没有协议的脚本,并且正在从文件系统运行:

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

此语法用于允许脚本在具有协议 https:// 的页面上通过 HTTPS 加载,并在具有协议 http:// 的页面上加载 HTTP 版本

它有一个不幸的副作用,即尝试加载但无法加载file://somecdn.somewhere.com...


解决 方案

在你调用(或任何与此相关的 DOM 方法)之前,请确保你想要访问的元素存在,即 DOM 已加载。getElementById

这可以通过简单地将 JavaScript 放在相应的 DOM 元素之后来确保

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

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

在这种情况下,您也可以将代码放在结束 body 标记 () 之前(所有 DOM 元素在执行脚本时都可用)。</body>

其他解决方案包括侦听加载 [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
});

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

jQuery的

首先确保 jQuery 已正确加载。使用浏览器的开发者工具找出是否找到了jQuery文件,如果没有,请更正URL(例如,在开头添加or方案,调整路径等)。http:https:

监听 / 事件正是 jQuery 使用 .ready() [docs] 所做的。所有影响 DOM 元素的 jQuery 代码都应该在该事件处理程序中。loadDOMContentLoaded

事实上,jQuery教程明确指出:

由于我们在使用jQuery时所做的几乎所有事情都是读取或操作文档对象模型(DOM),因此我们需要确保在DOM准备就绪后立即开始添加事件等。

为此,我们为文档注册一个就绪事件。

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

或者,您也可以使用速记语法:

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

两者都是等效的。

14赞 Nathan Bubna 12/7/2013 #3

正如@FelixKling所指出的,最有可能的情况是你正在寻找的节点(还不存在)。

但是,现代开发实践通常可以使用 DocumentFragments 或直接分离/重新附加当前元素来操作文档树之外的文档元素。这些技术可以用作 JavaScript 模板的一部分,或者在相关元素被大量更改时避免过多的重绘/重排操作。

类似地,在现代浏览器中推出的新的“Shadow DOM”功能允许元素成为文档的一部分,但不能通过document.getElementById及其所有同级方法(querySelector等)进行查询。这样做是为了封装功能并专门隐藏它。

不过,同样,您要查找的元素很可能根本不在文档中,您应该按照 Felix 的建议进行操作。但是,您还应该意识到,这越来越不是元素可能(暂时或永久)无法找到的唯一原因。

19赞 Sumit Kumar 1/7/2014 #4

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

  1. 指定了 id 的元素/DOM 尚不存在。
  2. 该元素存在,但未在 DOM 中注册 [如果从 Ajax 响应动态附加 HTML 节点]。
  3. 存在多个具有相同 ID 的元素,这会导致冲突。

解决 方案

  1. 尝试在元素声明后访问该元素,或者使用类似的东西$(document).ready();

  2. 对于来自 Ajax 响应的元素,请使用 jQuery 方法。旧版本的jQuery也有同样的功能。.bind().live()

  3. 使用工具[例如,浏览器的 webdeveloper 插件]查找重复的 ID 并将其删除。

16赞 George Mulligan 1/6/2016 #5

如果您尝试访问的元素位于 内部,并且您尝试在上下文之外访问它,这也将导致其失败。iframeiframe

如果你想在iframe中获取一个元素,你可以在这里找到方法。

12赞 CertainPerformance 5/10/2019 #6

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

  • getElementById要求传递的字符串是逐字的 ID,而不是其他任何内容。如果在传递的字符串前面加上 ,并且 ID 不以 a 开头,则不会选择任何内容:##

      <div id="foo"></div>
    
      // Error, selected element will be null:
      document.getElementById('#foo')
      // Fix:
      document.getElementById('foo')
    
  • 同样,对于 ,不要在传递的字符串前面加上 :getElementsByClassName.

      <div class="bar"></div>
    
      // Error, selected element will be undefined:
      document.getElementsByClassName('.bar')[0]
      // Fix:
      document.getElementsByClassName('bar')[0]
    
  • 使用 querySelector、querySelectorAll 和 jQuery,要将元素与特定类名匹配,请直接在类之前放置 a。同样,要将元素与特定 ID 匹配,请直接在 ID 之前放置 a:.#

      <div class="baz"></div>
    
      // Error, selected element will be null:
      document.querySelector('baz')
      $('baz')
      // Fix:
      document.querySelector('.baz')
      $('.baz')
    

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

  • 若要匹配具有两个或多个属性(如两个类名,或者一个类名和一个属性)的元素,请将每个属性的选择器放在选择器字符串中彼此相邻,不要用空格分隔它们(因为空格表示后代选择器)。例如,要选择:data-

      <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、类或属性进行选择,则大括号 like 或将不起作用。

0赞 Piotr Niewinski 7/6/2023 #7

我正在使用 Apache Tapestry,就我而言,上述答案都没有帮助。最后,我发现错误是由对“id”和“t:id”使用相同的值引起的。 我希望它有所帮助。