为什么jQuery或像getElementById这样的DOM方法找不到元素?
document.getElementById
, $("#id")
或任何其他DOM方法/ jQuery选择器找不到元素的可能原因是什么?
示例问题包括:
jQuery默默地无法绑定事件处理程序,并且返回null
的标准DOM方法导致错误:
未捕获的TypeError:无法设置null的属性“…”
当您的脚本运行时,您尝试查找的元素不在DOM中 。
DOM依赖脚本的位置会对其行为产生深远影响。 浏览器从上到下解析HTML文档。 元素被添加到DOM中,脚本(通常)会在遇到它们时执行。 这意味着订单很重要。 通常,脚本无法找到稍后出现在标记中的元素,因为这些元素尚未添加到DOM中。
考虑以下标记; 脚本#1无法找到
而脚本#2成功:
test div
简短而简单:因为您要查找的元素(尚未)存在于文档中。
对于本答案的其余部分,我将使用getElementById
作为示例,但这同样适用于getElementsByTagName
, querySelector
和任何其他选择元素的DOM方法。
可能的原因
元素可能不存在的原因有两个:
-
文档中不存在具有传递的ID的元素。 您应该仔细检查传递给
getElementById
的ID是否与(生成的)HTML中现有元素的ID匹配,并且您没有拼写错误 ID(ID 区分大小写 !)。顺便提一下,在大多数实现
querySelector()
和querySelectorAll()
方法的当代浏览器中 ,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...
的不幸副作用file://somecdn.somewhere.com...
解决方案
在调用getElementById
(或任何DOM方法)之前,请确保存在要访问的元素,即加载DOM。
只需将JavaScript放在相应的DOM元素之后即可确保这一点
在这种情况下,您也可以将代码放在结束体标记( )之前(所有DOM元素在脚本执行时都可用)。
其他解决方案包括监听load
[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(例如,在开头添加http:
或https:
scheme,调整路径等)
听取load
/ DOMContentLoaded
事件正是jQuery正在使用.ready()
[docs] 。 影响DOM元素的所有jQuery代码都应该在该事件处理程序中。
事实上, jQuery教程明确指出:
由于我们在使用jQuery时几乎所有操作都会读取或操作文档对象模型(DOM),因此我们需要确保在DOM准备就绪后立即开始添加事件等。
为此,我们为文档注册了一个ready事件。
$(document).ready(function() { // do stuff when DOM is ready });
或者,您也可以使用简写语法:
$(function() { // do stuff when DOM is ready });
两者都是等价的。
基于id的选择器不起作用的原因
- 指定了id的元素/ DOM尚不存在。
- 该元素存在,但它没有在DOM中注册[在从Ajax响应动态附加HTML节点的情况下]。
- 存在多个具有相同id的元素,这导致冲突。
解决方案
-
尝试在声明后访问元素,或者使用
$(document).ready();
-
对于来自Ajax响应的元素,请使用jQuery的
.bind()
方法。 旧版本的jQuery有相同的.live()
。 -
使用工具[例如,浏览器的webdeveloper插件]来查找重复的ID并将其删除。
正如@FelixKling指出的那样,最可能的情况是您正在寻找的节点(尚未)存在。
但是,现代开发实践通常可以使用DocumentFragments操作文档树之外的文档元素,或者直接分离/重新附加当前元素。 这些技术可以用作JavaScript模板的一部分,或者在所讨论的元素被大量改变时避免过多的重绘/重排操作。
类似地,在现代浏览器中推出的新“Shadow DOM”function允许元素成为文档的一部分,但不能通过document.getElementById及其所有兄弟方法(querySelector等)进行查询。 这样做是为了封装function并特别隐藏它。
但是,再一次,很可能你正在寻找的元素不是(还)在文档中,你应该按照Felix的建议去做。 但是,您还应该意识到,这一点越来越不是元素可能无法实现(临时或永久)的唯一原因。
如果您尝试访问的元素位于iframe
并且您尝试在iframe
的上下文之外访问它,这也会导致它失败。
如果你想在iframe中获取一个元素,你可以在这里找到它。
尝试将document.getElementById
放在setTimeout()
例如
setTimeout(function(){ console.log(document.getElementById('whatever')); }, 100);
如果这样可行,那么这只是一个时间问题。