为什么 jQuery 或诸如 getElementById 之类的 DOM 方法找不到该元素? [英] Why does jQuery or a DOM method such as getElementById not find the element?

查看:29
本文介绍了为什么 jQuery 或诸如 getElementById 之类的 DOM 方法找不到该元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

示例问题包括:

  • jQuery 静默无法绑定事件处理程序
  • jQuerygetter"方法 (.val(), .html(), .text()) 返回 undefined
  • 返回 null 的标准 DOM 方法会导致多个错误中的任何一个:
<块引用>

未捕获的类型错误:无法设置 null 的属性..."
未捕获的类型错误:无法设置 null 的属性(设置...")
未捕获的类型错误:无法读取 null 的属性..."
未捕获的类型错误:无法读取 null 的属性(读取...")

最常见的形式是:

<块引用>

未捕获的类型错误:无法设置 null 的属性onclick"
未捕获的类型错误:无法读取 null 的属性addEventListener"
未捕获的类型错误:无法读取 null 的属性样式"

解决方案

您尝试查找的元素不在 DOM 当您的脚本运行时.

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

考虑以下标记;脚本 #1 未能找到

而脚本 #2 成功:

<div id="test">测试 div</div><脚本>console.log("script #2:", document.getElementById("test"));//<div id="test" ...</script>

那么,你该怎么办?您有几个选择:

<小时>

选项 1:移动您的脚本

鉴于我们在上面的示例中看到的内容,一个直观的解决方案可能是简单地将您的脚本移到标记下方,越过您想要访问的元素.事实上,长期以来,将脚本放在页面底部被认为是最佳实践 出于各种原因.以这种方式组织,文档的其余部分将在执行您的脚本之前被解析:

<button id="test">点击我</button><脚本>document.getElementById("test").addEventListener("click", function() {console.log("点击:", this);});</body><!-- 结束正文标签-->

虽然这是有道理的,并且是旧浏览器的可靠选择,但它是有限的,并且有更灵活、更现代的方法可用.

<小时>

选项 2:defer 属性

虽然我们确实说过脚本是(通常)在遇到它们时执行",但现代浏览器允许您指定不同的行为.如果您要链接外部脚本,则可以使用 defer 属性.

<块引用>

[defer,一个布尔属性,] 被设置为向浏览器指示脚本将在文档被解析之后执行,但在触发 DOMContentLoaded.

这意味着您可以将带有 defer 标记的脚本放置在任何地方,甚至是 ,并且它应该可以访问完全实现的 DOM.>

<script src="https://gh-canon.github.io/misc-demos/log-test-click.js"延迟></script><button id="test">点击我</button>

记住...

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

<小时>

选项 3:模块

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

将脚本的type 设置为module,例如:

<button id="test">点击我</button>

<小时>

选项 4:推迟事件处理

为在解析文档后触发的事件添加侦听器.

DOMContentLoaded 事件

DOMContentLoaded在从初始解析完全构建 DOM 后触发,无需等待加载样式表或图像等内容.

<button id="test">点击我</button>

窗口:加载事件

加载 事件在 DOMContentLoaded 和其他资源(如样式表和图像)加载后触发.出于这个原因,它比我们的目的更晚触发.尽管如此,如果您正在考虑像 IE8 这样的旧浏览器,那么这种支持几乎是普遍的.当然,您可能需要 addEventListener().

<button id="test">点击我</button>

jQuery 的 ready()

DOMContentLoadedwindow:load 都有各自的注意事项.jQuery 的 ready() 提供了一种混合解决方案,使用 DOMContentLoaded 在可能的情况下,在必要时故障转移到 window:load,如果 DOM 已经完成,则立即触发其回调.

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><脚本>$(函数(){$("#test").click(function() {console.log("点击:", this);});});<button id="test">点击我</button>

<小时>

选项 5:事件委托

将事件处理委托给目标元素的祖先.

当一个元素引发一个事件时(前提是它是一个 冒泡 事件并且没有什么可以阻止它的传播),该元素祖先中的每个父级,一直到 window,也接收该事件.这允许我们将处理程序附加到现有元素并在事件从其后代冒泡时采样事件......甚至从附加处理程序之后添加的后代.我们所要做的就是检查事件是否由所需元素引发,如果是,则运行我们的代码.

通常,这种模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序.为了效率,选择目标元素最近的可靠祖先,而不是附加到 document.

原生 JavaScript

<div id="ancestor"><!-- 我们脚本可用的最近祖先 --><脚本>document.getElementById("ancestor").addEventListener("click", function(e) {如果(e.target.id ===后代"){console.log("点击:", e.target);}});<button id="descendant">点击我</button>

jQuery 的 on()

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div id="ancestor"><!-- 我们脚本可用的最近祖先 --><脚本>$("#ancestor").on("click", "#descendant", function(e) {console.log("点击:", this);});<button id="descendant">点击我</button>

What are the possible reasons for document.getElementById, $("#id") or any other DOM method / jQuery selector not finding the elements?

Example problems include:

  • jQuery silently failing to bind an event handler
  • jQuery "getter" methods (.val(), .html(), .text()) returning undefined
  • A standard DOM method returning null resulting in any of several errors:

Uncaught TypeError: Cannot set property '...' of null
Uncaught TypeError: Cannot set properties of null (setting '...')
Uncaught TypeError: Cannot read property '...' of null
Uncaught TypeError: Cannot read properties of null (reading '...')

The most common forms are:

Uncaught TypeError: Cannot set property 'onclick' of null
Uncaught TypeError: Cannot read property 'addEventListener' of null
Uncaught TypeError: Cannot read property 'style' of null

解决方案

The element you were trying to find wasn’t in the DOM when your script ran.

The position of your DOM-reliant script can have a profound effect upon its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (generally) executed as they're encountered. This means that order matters. Typically, scripts can't find elements which appear later in the markup because those elements have yet to be added to the DOM.

Consider the following markup; script #1 fails to find the <div> while script #2 succeeds:

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

So, what should you do? You've got a few options:


Option 1: Move your script

Given what we've seen in the example above, an intuitive solution might be to simply move your script down the markup, past the elements you'd like to access. In fact, for a long time, placing scripts at the bottom of the page was considered a best practice for a variety of reasons. Organized in this fashion, the rest of the document would be parsed before executing your script:

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

While this makes sense, and is a solid option for legacy browsers, it's limited and there are more flexible, modern approaches available.


Option 2: The defer attribute

While we did say that scripts are, "(generally) executed as they're encountered," modern browsers allow you to specify a different behavior. If you're linking an external script, you can make use of the defer attribute.

[defer, a Boolean attribute,] is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.

This means that you can place a script tagged with defer anywhere, even the <head>, and it should have access to the fully realized DOM.

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

Just keep in mind...

  1. defer can only be used for external scripts, i.e.: those having a src attribute.
  2. be aware of browser support, i.e.: buggy implementation in IE < 10


Option 3: Modules

Depending upon your requirements, you may be able to utilize JavaScript modules. Among other important distinctions from standard scripts (noted here), modules are deferred automatically and are not limited to external sources.

Set your script's type to module, e.g.:

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


Option 4: Defer with event handling

Add a listener to an event which fires after your document has been parsed.

DOMContentLoaded event

DOMContentLoaded fires after the DOM has been completely constructed from the initial parse, without waiting for things like stylesheets or images to load.

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

Window: load event

The load event fires after DOMContentLoaded and additional resources like stylesheets and images have been loaded. For that reason, it fires later than desired for our purposes. Still, if you're considering older browsers like IE8, the support is nearly universal. Granted, you may want a 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's ready()

DOMContentLoaded and window:load each have their caveats. jQuery's ready() delivers a hybrid solution, using DOMContentLoaded when possible, failing over to window:load when necessary, and firing its callback immediately if the DOM is already complete.

You can pass your ready handler directly to jQuery as $(handler), e.g.:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(function() {
    $("#test").click(function() {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>


Option 5: Event Delegation

Delegate the event handling to an ancestor of the target element.

When an element raises an event (provided that it's a bubbling event and nothing stops its propagation), each parent in that element's ancestry, all the way up to window, receives the event as well. That allows us to attach a handler to an existing element and sample events as they bubble up from its descendants... even from descendants added after the handler was attached. All we have to do is check the event to see whether it was raised by the desired element and, if so, run our code.

Typically, this pattern is reserved for elements which don't exist at load-time or to avoid attaching a large amount of duplicate handlers. For efficiency, select the nearest reliable ancestor of the target element rather than attaching to document.

Native 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's on()

jQuery makes this functionality available through on(). Given an event name, a selector for the desired descendant, and an event handler, it will resolve your delegated event handling and manage your this context:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></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>

这篇关于为什么 jQuery 或诸如 getElementById 之类的 DOM 方法找不到该元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆