为什么 jQuery 或 getElementById 等 DOM 方法找不到该元素?
document.getElementById
、$("#id")
或任何其他 DOM 方法/jQuery 选择器找不到元素的可能原因是什么?
示例问题包括:
- jQuery 默默地无法绑定事件处理程序
- jQuery“getter”方法(
.val()
、.html()
、.text()< /code>) 返回
undefined
- 标准 DOM 方法返回
null
导致以下几种错误之一:
未捕获类型错误:无法设置 null 属性“...”
未捕获的类型错误:无法设置 null 的属性(设置“...”)
未捕获的类型错误:无法读取 null 的属性“...”
未捕获的类型错误:无法读取 null 的属性(读取“...”)
最常见的形式是:
未捕获类型错误:无法将属性“onclick”设置为 null
未捕获的类型错误:无法读取 null 的属性“addEventListener”
未捕获的类型错误:无法读取 null 的属性“样式”
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()
) returningundefined
- 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您尝试查找的元素不在 DOM 当你的脚本运行时。
依赖 DOM 的脚本的位置可能对其行为产生深远的影响。浏览器从上到下解析 HTML 文档。元素被添加到 DOM 中,并且脚本(默认情况下)在遇到它们时执行。 这意味着顺序很重要。通常,脚本无法找到标记中稍后出现的元素,因为这些元素尚未添加到 DOM 中。
考虑以下标记;脚本 #1 找不到
而脚本 #2 成功:
那么,你应该做什么?您有几个选择:
选项 1:移动您的脚本
鉴于我们在上面的示例中看到的情况,一个直观的解决方案可能是简单地将您的脚本沿着标记向下移动,越过您想要访问的元素。事实上,长期以来,将脚本放置在页面底部被认为是最佳选择出于各种原因练习。以这种方式组织,在执行脚本之前将解析文档的其余部分:
虽然这是有道理的,并且对于旧版浏览器来说是一个可靠的选择,但它是有限的,并且有更灵活、更现代的方法可用。
选项 2:
defer
属性虽然我们确实说过脚本是“(默认情况下)在遇到脚本时执行”,但现代浏览器允许您指定不同的行为。如果您要链接外部脚本,则可以使用
defer
属性。这意味着您可以将带有
defer
标记的脚本放置在任何地方,甚至是,并且它应该能够访问完全实现的 DOM。
请记住...
defer
只能用于外部脚本,即:具有src
属性的脚本。选项 3:模块
根据您的要求,您可以使用 JavaScript 模块。与标准脚本的其他重要区别(此处注明),模块会自动延迟,并且不限于外部源。
将脚本的
type
设置为module
,例如:选项 4:延迟事件处理
将侦听器添加到解析文档后触发的事件。
DOMContentLoaded 事件
DOMContentLoaded
在 DOM 从初始解析完全构建完成后触发,无需等待样式表或图像等内容加载。窗口:加载事件
load
< /a> 事件在DOMContentLoaded
以及加载样式表和图像等其他资源后触发。因此,它的触发时间比我们预期的要晚。不过,如果您考虑使用 IE8 等较旧的浏览器,那么支持几乎是普遍的。当然,您可能需要一个 polyfill 用于addEventListener()
。jQuery 的
ready()
DOMContentLoaded
和window:load
各有其注意事项。 jQuery 的ready()
使用DOMContentLoaded 提供混合解决方案
如果可能,在必要时故障转移到window:load
,并在 DOM 已经完成时立即触发其回调。您可以将就绪处理程序作为
$(handler)
直接传递给 jQuery,例如:选项 5:事件委托
将事件处理委托给目标元素的祖先。
当元素引发事件时(前提是它是 冒泡事件并且没有任何东西阻止其传播),该元素祖先中的每个父元素,一直到
window
,也接收该事件。这允许我们将处理程序附加到现有元素,并在事件从其后代中冒出时对事件进行采样...甚至是在附加处理程序后添加的后代中。我们所要做的就是检查事件以查看它是否是由所需元素引发的,如果是,则运行我们的代码。通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,请选择目标元素最近的可靠祖先,而不是将其附加到
文档
。如果合适,不要忘记Event:stopPropagation( )
以防止进一步冒泡/处理。原生 JavaScript
jQuery 的
on()
jQuery 通过
on()
提供此功能代码>。给定事件名称、所需后代的选择器和事件处理程序,它将解析您的委托事件处理并管理您的this
上下文: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 on its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (by default) executed as they're encountered. This means that order matters. Typically, scripts can't find elements that 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: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:
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
attributeWhile we did say that scripts are, "(by default) 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.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.Just keep in mind...
defer
can only be used for external scripts, i.e.: those having asrc
attribute.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
tomodule
, e.g.:Option 4: Defer with event handling
Add a listener to an event that 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.Window: load event
The
load
event fires afterDOMContentLoaded
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 foraddEventListener()
.jQuery's
ready()
DOMContentLoaded
andwindow:load
each have their caveats. jQuery'sready()
delivers a hybrid solution, usingDOMContentLoaded
when possible, failing over towindow: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.: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 that don't exist at load time or to avoid attaching a large number of duplicate handlers. For efficiency, select the nearest reliable ancestor of the target element rather than attaching it to the
document
. If appropriate, don't forgetEvent:stopPropagation()
to prevent further bubbling/processing.Native JavaScript
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 yourthis
context:简短:因为您要查找的元素在文档中尚不存在。
对于这个答案的其余部分,我将使用
getElementById
为例,但这同样适用于getElementsByTagName
,querySelector
以及任何其他选择元素的 DOM 方法。可能的原因
元素可能不存在的原因有以下三个:
安文档中确实不存在具有传递的 ID 的元素。您应该仔细检查您传递给
getElementById
的 ID 是否确实与(生成的)HTML 中现有元素的 ID 匹配,并且您没有拼写错误该 ID(ID 是区分大小写!)。如果您使用
getElementById
,请确保您仅提供元素的 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")< /代码>)。
您调用
getElementById
时,该元素不存在。尽管您可以在页面上看到该元素,但该元素并不位于您正在查询的文档中,因为它位于
iframe
中(这是它自己的文档)。当您搜索包含iframe
中的元素的文档时,不会搜索这些元素。如果问题是原因 3(位于
iframe
或类似文件中),您需要查看iframe
中的文档,而不是父文档,也许可以通过获取 < code>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 标记之前 (
) (所有 DOM 元素在脚本执行时都可用)。
其他解决方案包括监听
load
[MDN] 或DOMContentLoaded
[MDN] 事件。在这些情况下,将 JavaScript 代码放置在文档中的哪个位置并不重要,只需记住将所有 DOM 处理代码放置在事件处理程序中即可。示例:
请参阅 quirksmode.org 上的文章,了解有关事件处理和浏览器差异的更多信息。
jQuery
首先确保 jQuery 已正确加载。 使用浏览器的开发者工具查明是否找到 jQuery 文件并更正URL 如果不是(例如在开头添加
http:
或https:
方案,调整路径等)
load
/DOMContentLoaded
事件正是 jQuery 使用 < 所做的事情code>.ready() [文档]。所有影响 DOM 元素的 jQuery 代码都应该位于该事件处理程序内。事实上,jQuery 教程明确指出:
或者,您也可以使用简写语法:
两者是等效的。
Short and simple: Because the elements you are looking for do not exist in the document (yet).
For the remainder of this answer I will use
getElementById
for examples, but the same applies togetElementsByTagName
,querySelector
, and any other DOM method that selects elements.Possible Reasons
There are three reasons why an element might not exist:
An element with the passed ID really does not exist in the document. You should double check that the ID you pass to
getElementById
really matches an ID of an existing element in the (generated) HTML and that you have not misspelled the ID (IDs are case-sensitive!).If you're using
getElementById
, be sure you're only giving the ID of the element (e.g.,document.getElemntById("the-id")
). If you're using a method that accepts a CSS selector (likequerySelector
), be sure you're including the#
before the ID to indicate you're looking for an ID (e.g.,document.querySelector("#the-id")
). You must not use the#
withgetElementById
, and must use it withquerySelector
and similar. Also note that if the ID has characters in it that aren't valid in CSS identifiers (such as a.
;id
attributes containing.
characters are poor practice, but valid), you have to escape those when usingquerySelector
(document.querySelector("#the\\.id")
)) but not when usinggetElementById
(document.getElementById("the.id")
).The element does not exist at the moment you call
getElementById
.The element isn't in the document you're querying even though you can see it on the page, because it's in an
iframe
(which is its own document). Elements iniframes
aren't searched when you search the document that contains them.If the problem is reason 3 (it's in an
iframe
or similar), you need to look through the document in theiframe
, not the parent document, perhaps by getting theiframe
element and using itscontentDocument
property to access its document (same-origin only). The rest of this answer addresses the first two reasons.The second reason — it's not there yet — is quite common. Browsers parse and process the HTML from top to bottom. That means that any call to a DOM element which occurs before that DOM element appears in the HTML, will fail.
Consider the following example:
The
div
appears after thescript
. At the moment the script is executed, the element does not exist yet andgetElementById
will returnnull
.jQuery
The same applies to all selectors with jQuery. jQuery won't find elements if you misspelled your selector or you are trying to select them before they actually exist.
An added twist is when jQuery is not found because you have loaded the script without protocol and are running from file system:
this syntax is used to allow the script to load via HTTPS on a page with protocol https:// and to load the HTTP version on a page with protocol http://
It has the unfortunate side effect of attempting and failing to load
file://somecdn.somewhere.com...
Solutions
Before you make a call to
getElementById
(or any DOM method for that matter), make sure the elements you want to access exist, i.e. the DOM is loaded.This can be ensured by simply putting your JavaScript after the corresponding DOM element
in which case you can also put the code just before the closing body tag (
</body>
) (all DOM elements will be available at the time the script is executed).Other solutions include listening to the
load
[MDN] orDOMContentLoaded
[MDN] events. In these cases it does not matter where in the document you place the JavaScript code, you just have to remember to put all DOM processing code in the event handlers.Example:
Please see the articles at quirksmode.org for more information regarding event handling and browser differences.
jQuery
First make sure that jQuery is loaded properly. Use the browser's developer tools to find out whether the jQuery file was found and correct the URL if it wasn't (e.g. add the
http:
orhttps:
scheme at the beginning, adjust the path, etc.)Listening to the
load
/DOMContentLoaded
events is exactly what jQuery is doing with.ready()
[docs]. All your jQuery code that affects DOM element should be inside that event handler.In fact, the jQuery tutorial explicitly states:
Alternatively you can also use the shorthand syntax:
Both are equivalent.
基于 id 的选择器不起作用的原因
解决方案
尝试在声明后访问该元素,或者使用诸如
$(document).ready();
对于来自 Ajax 响应的元素,使用 jQuery 的
.bind()
方法。旧版本的 jQuery 具有相同的.live()
。使用工具[例如浏览器的 webdeveloper 插件]查找重复的 id 并将其删除。
Reasons why id based selectors don't work
Solutions
Try to access the element after its declaration or alternatively use stuff like
$(document).ready();
For elements coming from Ajax responses, use the
.bind()
method of jQuery. Older versions of jQuery had.live()
for the same.Use tools [for example, webdeveloper plugin for browsers] to find duplicate ids and remove them.
如果您尝试访问的元素位于
iframe
内部,并且您尝试在iframe
上下文之外访问它,这也会导致其失败。如果您想获取 iframe 中的元素,可以在此处了解如何操作。
If the element you are trying to access is inside an
iframe
and you try to access it outside the context of theiframe
this will also cause it to fail.If you want to get an element in an iframe you can find out how here.
正如@FelixKling 指出的,最有可能的情况是您正在寻找的节点(尚不存在)。
然而,现代开发实践通常可以使用 DocumentFragments 或直接直接分离/重新附加当前元素来操作文档树外部的文档元素。此类技术可用作 JavaScript 模板的一部分,或在相关元素发生大量更改时避免过多的重绘/回流操作。
同样,在现代浏览器中推出的新“Shadow DOM”功能允许元素成为文档的一部分,但不能通过 document.getElementById 及其所有同级方法(querySelector 等)进行查询。这样做是为了封装功能并专门隐藏它。
不过,同样,您要查找的元素很可能根本不在文档中,您应该按照 Felix 的建议进行操作。但是,您还应该意识到,这越来越不是元素可能无法找到(无论是暂时还是永久)的唯一原因。
As @FelixKling pointed out, the most likely scenario is that the nodes you are looking for do not exist (yet).
However, modern development practices can often manipulate document elements outside of the document tree either with DocumentFragments or simply detaching/reattaching current elements directly. Such techniques may be used as part of JavaScript templating or to avoid excessive repaint/reflow operations while the elements in question are being heavily altered.
Similarly, the new "Shadow DOM" functionality being rolled out across modern browsers allows elements to be part of the document, but not query-able by document.getElementById and all of its sibling methods (querySelector, etc.). This is done to encapsulate functionality and specifically hide it.
Again, though, it is most likely that the element you are looking for simply is not (yet) in the document, and you should do as Felix suggests. However, you should also be aware that that is increasingly not the only reason that an element might be unfindable (either temporarily or permanently).
如果脚本执行顺序不是问题,则问题的另一个可能原因是未正确选择元素:
getElementById
要求传递的字符串是 ID 逐字,没有别的。如果您在传递的字符串前添加#
前缀,并且 ID 不以#
开头,则不会选择任何内容:同样,对于
getElementsByClassName< /code>,不要在传递的字符串前添加
.
:使用 querySelector、querySelectorAll 和 jQuery,来匹配具有特定类名的元素,将
.
直接放在类之前。同样,要匹配具有特定 ID 的元素,请直接在 ID 前面放置#
:在大多数情况下,这里的规则与 CSS 选择器的规则相同,可以详细查看 此处。
要匹配具有两个或多个属性(例如两个类名,或一个类名和一个
data-
属性)的元素,请将每个属性的选择器放在选择器中彼此相邻的位置字符串,没有空格分隔它们(因为空格表示 后代选择器)。例如,选择:使用查询字符串
.foo.bar
。选择使用查询字符串
.foo[data-bar="someData"]
。要选择下面的:
使用
div.parent> span[data-username="bob"]
.大小写和拼写对于上述所有内容都很重要。如果大小写不同,或者拼写不同,则不会选择该元素:
您还需要确保方法具有正确的大小写和拼写。使用以下之一:
<前><代码>$(选择器)
文档.querySelector
文档.querySelectorAll
document.getElementsByClassName
document.getElementsByTagName
document.getElementById
任何其他拼写或大小写都不起作用。例如,
document.getElementByClassName
将引发错误。确保将字符串传递给这些选择器方法。如果您将非字符串的内容传递给
querySelector
、getElementById
等,它几乎肯定不会起作用。如果要选择的元素上的 HTML 属性被引号括起来,则它们必须是纯直引号(单引号或双引号);如果您尝试按 ID、类或属性进行选择,则像
'
或”
这样的大引号将不起作用。If script execution order is not the issue, another possible cause of the problem is that the element is not being selected properly:
getElementById
requires the passed string to be the ID verbatim, and nothing else. If you prefix the passed string with a#
, and the ID does not start with a#
, nothing will be selected:Similarly, for
getElementsByClassName
, don't prefix the passed string with a.
:With querySelector, querySelectorAll, and jQuery, to match an element with a particular class name, put a
.
directly before the class. Similarly, to match an element with a particular ID, put a#
directly before the ID:The rules here are, in most cases, identical to those for CSS selectors, and can be seen in detail here.
To match an element which has two or more attributes (like two class names, or a class name and a
data-
attribute), put the selectors for each attribute next to each other in the selector string, without a space separating them (because a space indicates the descendant selector). For example, to select:use the query string
.foo.bar
. To selectuse the query string
.foo[data-bar="someData"]
. To select the<span>
below:use
div.parent > span[data-username="bob"]
.Capitalization and spelling does matter for all of the above. If the capitalization is different, or the spelling is different, the element will not be selected:
You also need to make sure the methods have the proper capitalization and spelling. Use one of:
Any other spelling or capitalization will not work. For example,
document.getElementByClassName
will throw an error.Make sure you pass a string to these selector methods. If you pass something that isn't a string to
querySelector
,getElementById
, etc, it almost certainly won't work.If the HTML attributes on elements you want to select are surrounded by quotes, they must be plain straight quotes (either single or double); curly quotes like
‘
or”
will not work if you're trying to select by ID, class, or attribute.我正在使用 Apache Tapestry,就我而言,上述答案都没有帮助。最后,我发现错误是由于“id”和“t:id”使用相同的值引起的。
我希望它有帮助。
I was using Apache Tapestry, and in my case none of the above answers helped. Finally, I found out that the error was caused by using the same value for the 'id' and 't:id'.
I hope it helps.