document.getElementById
, $("#id")
或任何其他 DOM 方法/jQuery 选择器找不到元素的可能原因是什么?
示例问题包括:
- jQuery 静默绑定事件处理程序失败
- jQuery“ Getter”方法(
.val()
,.html()
,.text()
undefined
- 返回
null
的标准 DOM 方法会导致多种错误:
未捕获的类型错误:无法设置 null 的属性“…”
未捕获的类型错误:无法设置 null 的属性(设置“…”)
未捕获的类型错误:无法读取 null 的属性“…”
未捕获的 TypeError:无法读取 null 的属性(正在读取“…”)
最常见的形式是:
未捕获的类型错误:无法设置 null 的属性“onclick”
未捕获的类型错误:无法读取 null 的属性“addEventListener”
未捕获的类型错误:无法读取 null 的属性“样式”
原文由 Felix Kling 发布,翻译遵循 CC BY-SA 4.0 许可协议
当您的脚本运行时,您试图查找的元素不在 DOM 中。
依赖 DOM 的脚本的位置对其行为有深远的影响。浏览器从上到下解析 HTML 文档。元素被添加到 DOM 中,脚本(通常)在遇到时执行。 这意味着顺序很重要。 通常,脚本无法找到标记中稍后出现的元素,因为这些元素尚未添加到 DOM。
考虑以下标记;脚本 #1 找不到
<div>
而脚本 #2 成功:那你该怎么办?你有几个选择:
选项 1:移动脚本
考虑到我们在上面的示例中看到的情况,一个直观的解决方案可能是简单地将脚本向下移动标记,经过您想要访问的元素。事实上,很长一段时间以来,出于各种原因,将脚本放在页面底部被认为是 最佳实践。以这种方式组织,文档的其余部分将在执行脚本之前被解析:
虽然这是有道理的,并且是旧版浏览器的可靠选择,但它是有限的,并且有更灵活、更现代的方法可用。
选项 2:
defer
属性虽然我们确实说过脚本是 “(通常)在遇到它们时执行的”,但 现代浏览器允许您指定不同的行为。如果您要链接外部脚本,则可以使用
defer
属性。这意味着您可以将标记为
defer
的脚本放在任何地方,甚至是<head>
,它应该可以访问完全实现的 DOM。请记住…
defer
只能用于外部脚本,即:具有src
属性的脚本。选项 3:模块
根据您的要求,您可以使用 JavaScript 模块。除了与标准脚本( 此处注明)的其他重要区别之外,模块会自动延迟并且不限于外部源。
将脚本的
type
设置为module
,例如:选项 4:延迟事件处理
将侦听器添加到在解析文档后触发的事件。
DOMContentLoaded 事件
DOMContentLoaded
在从初始解析完全构建 DOM 后触发,无需等待样式表或图像等内容加载。窗口:加载事件
load
事件在DOMContentLoaded
之后触发,并且加载了样式表和图像等其他资源。出于这个原因,它的触发时间比我们预期的要晚。不过,如果您正在考虑使用 IE8 等较旧的浏览器,则支持几乎是普遍的。当然,您可能需要addEventListener()
的 polyfill 。jQuery 的
ready()
DOMContentLoaded
和window:load
各有注意事项。 jQuery 的ready()
提供了一个混合解决方案,使用DOMContentLoaded
在可能的情况下,故障转移到window:load
如果需要,它的回调已经完成,并且立即触发。您可以将准备好的处理程序作为
$(handler)
直接传递给 jQuery,例如:选项 5:事件委托
将事件处理委托给目标元素的祖先。
当一个元素引发一个事件(假设它是一个 冒泡 事件并且没有什么可以阻止它的传播)时,该元素祖先中的每个父元素,一直到
window
,也会接收到该事件。这允许我们将处理程序附加到现有元素,并在事件从其后代冒泡时对事件进行采样……甚至是在附加处理程序后添加的后代。我们所要做的就是检查事件以查看它是否由所需元素引发,如果是,则运行我们的代码。通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,选择目标元素最近的可靠祖先而不是将其附加到
document
。原生 JavaScript
jQuery 的
on()
jQuery 通过
on()
使这个功能可用。给定事件名称、所需后代的选择器和事件处理程序,它将解析您的委托事件处理并管理您的this
上下文: