document.getElementById
, $("#id")
或任何其他DOM方法/ jQuery选择器找不到元素的可能原因是什么?
示例问题包括:
-
jQuery默默地无法绑定事件处理程序
-
jQuery "getter"方法(
.val()
,.html()
,.text()
)返回undefined
-
返回
null
的标准DOM方法导致以下任何错误:
未捕获的TypeError:无法设置null的属性'...'未捕获TypeError:无法读取null的属性'...'
最常见的形式是:
未捕获的TypeError:无法设置null的属性'onclick'未捕获TypeError:无法读取null的属性'addEventListener'未捕获的TypeError:无法读取null的属性'style'
6 回答
当您的脚本运行时,您尝试查找的元素不在DOM中 .
DOM依赖脚本的位置会对其行为产生深远的影响 . 浏览器从上到下解析HTML文档 . 元素被添加到DOM中,脚本(通常)在遇到它们时执行 . This means that order matters. 通常,脚本无法找到稍后出现在标记中的元素,因为这些元素尚未添加到DOM中 .
考虑以下标记;脚本#1无法找到
<div>
而脚本#2成功:那你该怎么办?你有几个选择:
选项1:移动脚本
将您的脚本向下移动到页面下方,就在关闭正文标记之前 . 以这种方式组织,在脚本执行之前解析文档的其余部分:
注意:在底部放置脚本通常被认为是最佳实践 .
选项2:jQuery就绪()
使用ready()推迟脚本,直到完全解析DOM为止:
注意:您可以简单地绑定到DOMContentLoaded或window.onload,但每个都有其警告 . jQuery的ready()提供了一个混合解决方案 .
选项3:事件委托
当一个元素引发一个事件时(假设它是bubbling事件而没有任何东西停止它的传播),该元素的祖先中的每个父元素也会接收该事件 . 这允许我们将一个处理程序附加到现有元素,并在事件从它的后代冒泡时对它们进行冒泡...甚至是在附加处理程序之后添加的事件 . 我们所要做的就是检查事件是否由所需元素引发,如果是,则运行我们的代码 .
jQuery的on()为我们执行了这个逻辑 . 我们只提供一个事件名称,所需后代的选择器和一个事件处理程序:
注意:通常,此模式保留用于在加载时不存在的元素或避免附加大量处理程序 . 值得指出的是,虽然我已将一个处理程序附加到文档(出于演示目的),但您应该选择最近的可靠祖先 .
选项4:延迟属性
使用
<script>
的defer属性 .作为参考,这是来自external script的代码:
注意:defer属性当然看起来像一个神奇的子弹,但重要的是要注意注意事项...... 1.延迟只能用于外部脚本,即:具有src属性的脚本 . 2.注意浏览器支持,即:IE <10中的错误实现
Short and simple: 因为您要查找的元素(尚未)存在于文档中 .
对于本答案的其余部分,我将使用
getElementById
作为示例,但这同样适用于getElementsByTagName,querySelector以及任何其他选择元素的DOM方法 .Possible Reasons
元素可能不存在的原因有两个:
getElementById
的ID是否与(生成的)HTML中现有元素的ID相匹配,并且您没有拼写错误ID(ID区分大小写!) .顺便提一下,在实现
querySelector()
和querySelectorAll()
方法的majority of contemporary browsers中,CSS样式表示法用于通过id
检索元素,例如:document.querySelector('#elementID')
,而不是在document.getElementById('elementID')
下由id
检索元素的方法;在第一个字符中,#
字符是必不可少的,在第二个字符中,它会导致元素无法被检索 .getElementById
时,该元素不存在 .后一种情况很常见 . 浏览器从上到下解析和处理HTML . 这意味着在该DOM元素出现在HTML之前对DOM元素的任何调用都将失败 .
请考虑以下示例:
div
出现在script
之后 . 执行脚本时,元素尚不存在,getElementById
将返回null
.jQuery
这同样适用于使用jQuery的所有选择器 . 如果拼错了选择器,或者在实际存在之前尝试选择它们,jQuery将找不到元素 .
一个额外的转折是找不到jQuery,因为你已经加载了没有协议的脚本并且正在从文件系统运行:
此语法用于允许脚本通过HTTPS在具有协议https://的页面上加载,并在具有协议http://的页面上加载HTTP版本
它有尝试和未能加载
file://somecdn.somewhere.com...
的不幸副作用Solutions
在调用
getElementById
(或任何DOM方法)之前,请确保存在要访问的元素,即加载DOM .只需将JavaScript放在相应的DOM元素之后即可确保这一点
在这种情况下,您也可以将代码放在结束体标记(
</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明确指出:
或者,您也可以使用简写语法:
两者都是等价的 .
Reasons why id based selectors don't work
指定了id的元素/ DOM尚不存在 .
元素存在,但它没有在DOM中注册[如果从Ajax响应动态附加HTML节点] .
存在多个具有相同ID的元素,这会导致冲突 .
Solutions
尝试在声明后访问元素,或者使用像
$(document).ready();
这样的东西对于来自Ajax响应的元素,请使用jQuery的
.bind()
方法 . 较旧版本的jQuery也有.live()
.使用工具[例如,浏览器的webdeveloper插件]查找重复的ID并将其删除 .
正如@FelixKling指出的那样,最可能的情况是您正在寻找的节点(尚未存在) .
但是,现代开发实践通常可以使用DocumentFragments操作文档树之外的文档元素,或者直接分离/重新附加当前元素 . 这些技术可以用作JavaScript模板的一部分,或者在所讨论的元素被大量改变时避免过多的重绘/重排操作 .
类似地,在现代浏览器中推出的新“Shadow DOM”功能允许元素成为文档的一部分,但不能通过document.getElementById及其所有兄弟方法(querySelector等)进行查询 . 这样做是为了封装功能并特别隐藏它 .
但是,再一次,很可能你正在寻找的元素不是(还)在文档中,你应该按照Felix的建议去做 . 但是,你也应该如此意识到这一点越来越不是元素可能无法实现(临时或永久)的唯一原因 .
如果您尝试访问的元素位于
iframe
内,并且您尝试在iframe
的上下文之外访问它,这也会导致它失败 .如果你想在iframe中获取一个元素,你可以找到here .
尝试将
document.getElementById
放入setTimeout()
例如 .
如果这样可行,那么这只是一个时间问题 .