document.getElementById
、$("#id")
或任何其他DOM方法/ jQuery选择器找不到元素的可能原因是什么?
示例问题包括:
- jQuery静默绑定事件处理程序失败
- jQuery“getter”方法(
.val()
,.html()
,.text()
)返回undefined
- 返回
null
的标准DOM方法会导致以下任何错误:
未捕获的类型错误:无法设置null的属性“...”
未捕获的类型错误:无法设置null的属性(设置“...”)
未捕获的类型错误:无法读取null的属性“...”
未捕获的类型错误:无法读取null的属性(阅读“...”)
最常见的形式有:
未捕获的类型错误:无法将属性“onclick”设置为null
未捕获的类型错误:无法读取空值的属性“addEventListener
未捕获的类型错误:无法读取空值的属性“style
6条答案
按热度按时间hrirmatl1#
当脚本运行时,您试图查找的元素不在DOM中。
依赖DOM的脚本的位置会对其行为产生深远的影响。浏览器从上到下解析HTML文档。元素被添加到DOM,脚本(通常)在遇到它们时执行。**这意味着顺序很重要。**通常,脚本无法找到稍后出现在标记中的元素,因为这些元素尚未添加到DOM。
考虑以下标记;脚本#1无法找到
<div>
,而脚本#2成功:那么,你该怎么做呢?你有几个选择:
选项1:移动您的脚本
鉴于我们在上面的例子中所看到的,一个直观的解决方案可能是简单地将脚本沿着标记向下移动,经过您想要访问的元素。事实上,很长一段时间以来,出于各种原因,将脚本放置在页面底部被认为是最佳实践。以这种方式组织,文档的其余部分将在执行脚本之前进行解析:
虽然这是有意义的,并且是传统浏览器的可靠选择,但它是有限的,并且有更灵活的现代方法可用。
选项2:
defer
属性虽然我们说过脚本是,“(通常)在遇到它们时执行”, 现代浏览器允许您指定不同的行为。如果您链接外部脚本,您可以使用
defer
属性。[
defer
,布尔属性]被设置为向浏览器指示脚本将在文档被解析之后但在触发DOMContentLoaded
之前执行。这意味着您可以将标记为
defer
的脚本放置在任何地方,甚至是<head>
,并且它应该可以访问完全实现的DOM。只要记住...
defer
只能用于外部脚本,即:具有src
属性的那些。1.注意browser support,即:IE〈10中的错误实现
选项3:模块
根据您的需求,您可以使用JavaScript模块。与标准脚本的其他重要区别(在此注明)中,模块会自动延迟,并且不限于外部源。
将脚本的
type
设置为module
,例如:选项四:延迟事件处理
将侦听器添加到在分析文档后触发的事件。
DOMContentLoaded事件
DOMContentLoaded
在DOM从初始解析中完全构造完成后触发,而不等待样式表或图像等加载。窗口:加载事件
load
事件在DOMContentLoaded
和其他资源(如样式表和图像)加载后触发。因此,它的触发时间比我们预期的要晚。不过,如果您考虑的是IE8等旧浏览器,则支持几乎是通用的。当然,您可能需要polyfill foraddEventListener()
。jQuery的
ready()
jQuery的
ready()
提供了一个混合解决方案,尽可能使用DOMContentLoaded
,必要时故障转移到window:load
,如果DOM已经完成,则立即触发其回调。你可以将你的ready handler作为
$(*handler*)
直接传递给jQuery,例如:选项五:活动委托
将事件处理委托给目标元素的祖先。
当元素引发事件时(假设它是一个冒泡事件,并且没有任何东西阻止它的传播),该元素祖先中的每个父元素,一直到
window
,这允许我们将一个处理程序附加到一个现有的元素上,并在事件从它的后代中冒泡时对事件进行采样......甚至是从附加处理程序后添加的后代中。我们所要做的就是检查事件,看看它是否是由所需的元素引发的,如果是,则运行我们的代码。通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,请选择目标元素最近的可靠祖先,而不是将其附加到
document
。原生JavaScript
jQuery的
on()
jQuery通过
on()
提供了此功能。给定事件名称,所需后代的选择器和事件处理程序,它将解析您的委托事件处理并管理您的this
上下文:axzmvihb2#
**简短:**因为您要查找的元素在文档中不存在(尚未)。
getElementById
作为示例,但这同样适用于getElementsByTagName
,querySelector
和任何其他选择元素的DOM方法。可能原因
元素可能不存在的原因有三个:
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
属性是不好的做法,但有效),在使用querySelector
(document.querySelector("#the\\.id")
)时必须转义这些属性,但在使用getElementById
(document.getElementById("the.id")
)时不必转义。1.元素在您调用
getElementById
时不存在。1.即使您可以在页面上看到该元素,它也不在您正在查询的文档中,因为它位于
iframe
(它是自己的文档)中。当您搜索包含iframes
中的元素的文档时,不会搜索它们。如果问题是原因3(它在
iframe
或类似的文件中),您需要查看iframe
中的文档,而不是父文档,可能是通过获取iframe
元素并使用其contentDocument
属性来访问其文档(仅限同源)。第二个原因-它还没有出现-是很常见的。浏览器从上到下解析和处理HTML。这意味着在DOM元素出现在HTML中之前发生的任何对DOM元素的调用都将失败。
请看下面的例子:
div
出现在script
之后。在脚本执行时,该元素还不存在,getElementById
将返回null
。jQuery软件
这同样适用于jQuery中的所有选择器。如果你 * 拼错了 * 你的选择器,或者你试图 * 在它们实际存在之前 * 选择它们,jQuery将无法找到它们。
另一种情况是,由于您加载了没有协议的脚本并且从文件系统运行,因此找不到jQuery:
此语法用于允许脚本通过HTTPS加载到协议为https://的页面上,并在协议为http://的页面上加载HTTP版本
它具有尝试加载
file://somecdn.somewhere.com...
但失败的副作用解决方案
在调用
getElementById
(或任何DOM方法)之前,请确保要访问的元素存在,即DOM已加载。这可以通过简单地将JavaScript * 放在 * 相应的DOM元素之后来确保
在这种情况下,您也可以将代码放在结束body标记(
</body>
)之前(所有DOM元素在脚本执行时都可用)。其他解决方案包括监听
load
[MDN]或DOMContentLoaded
[MDN]事件。在这些情况下,将JavaScript代码放在文档中的哪个位置并不重要,您只需记住将所有DOM处理代码放在事件处理程序中。示例:
有关事件处理和浏览器差异的更多信息,请参阅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准备就绪,我们就开始添加事件等。
为此,我们为文档注册一个就绪事件。
或者,您也可以使用简写语法:
两者等同。
hfwmuf9z3#
基于id的选择器不起作用的原因
1.指定id的元素/DOM尚不存在。
1.该元素存在,但未在DOM中注册[在从 AJAX 响应动态追加HTML节点的情况下]。
1.存在多个具有相同id的元素,这会导致冲突。
解决方案
1.尝试在元素声明之后访问它,或者使用类似
$(document).ready();
的东西1.对于来自 AJAX 响应的元素,使用jQuery的
.bind()
方法。旧版本的jQuery有.live()
方法。1.使用工具[例如,用于浏览器的webdeveloper插件]查找重复的ID并删除它们。
jpfvwuh44#
如果您试图访问的元素位于
iframe
内部,并且您试图在iframe
的上下文之外访问它,这也会导致它失败。如果你想在iframe中获取一个元素,你可以找到here。
t30tvxxf5#
正如@FelixKling所指出的,最有可能的情况是您正在寻找的节点不存在(尚未)。
然而,现代开发实践通常可以使用DocumentFragments或简单地直接分离/重新附加当前元素来操作文档树之外的文档元素。这些技术可以用作JavaScript模板的一部分,或者在元素被大量更改时避免过多的重绘/回流操作。
类似地,在现代浏览器中推出的新的“Shadow DOM”功能允许元素成为文档的一部分,但不能通过document.getElementById及其所有兄弟方法(querySelector等)进行查询。这样做是为了封装功能并专门隐藏它。
同样,尽管如此,最有可能的情况是,您正在寻找的元素根本不在文档中,您应该按照Felix的建议去做。然而,您也应该意识到,这越来越不是元素可能无法找到的唯一原因(无论是暂时的还是永久的)。
xytpbqjk6#
如果脚本执行顺序不是问题,则问题的另一个可能原因是未正确选择元素:
getElementById
要求传入的字符串必须是ID verbatim,而没有其他任何内容。如果您在传入的字符串前面加上#
,并且ID不是以#
开头,则不会选择任何内容:getElementsByClassName
,不要在传入的字符串前面加上.
:一个二个一个一个
.
直接放在类之前。类似地,要匹配具有特定ID的元素,请将#
直接放在ID之前:在大多数情况下,这里的规则与CSS选择器的规则相同,可以在here中详细查看。
data-
属性),请将每个属性的选择器放在选择器字符串中彼此相邻,* 不使用 * 空格分隔它们(因为空格表示descendant selector)。例如,要选择:使用查询字符串
.foo.bar
。要选择使用查询字符串
.foo[data-bar="someData"]
。要选择下面的<span>
:使用
div.parent > span[data-username="bob"]
。任何其他拼写或大写都不起作用。例如,
document.getElementByClassName
将抛出错误。querySelector
,getElementById
等传递了一些不是字符串的东西,它几乎肯定不会工作。‘
或”
等花引号将不起作用。