什么是文件可能的原因。getElementById, $(“#id”)或任何其他DOM方法/ jQuery选择器找不到元素?

示例问题包括:

jQuery无声地绑定事件处理程序失败 jQuery“getter”方法(.val(), .html(), .text())返回undefined 一个标准的DOM方法返回null,导致以下任何错误:

无法设置属性'…null的' 无法设置null属性(设置'…') 无法读取属性…null的' 无法读取null属性(读取'…')

最常见的形式是:

无法将属性“onclick”设置为空 无法读取属性“addEventListener”为空 无法读取属性'style'为null


当脚本运行时,您试图查找的元素不在DOM中。

依赖于dom的脚本的位置会对其行为产生深远的影响。浏览器从上到下解析HTML文档。元素被添加到DOM中,脚本(通常)在遇到它们时执行。这意味着顺序很重要。通常,脚本无法找到稍后出现在标记中的元素,因为这些元素还没有添加到DOM中。

考虑下面的标记;脚本#1无法找到<div>,而脚本#2成功:

<脚本> console.log("脚本#1:",document.getElementById("test"));/ /空 > < /脚本 <div id="test">test div</div> <脚本> console.log("脚本#2:",document.getElementById("test"));// <div id="test"… > < /脚本

那么,你应该怎么做呢?你有几个选择:


选项1:移动脚本

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: <body> <button id="test">click me</button> <script> document.getElementById("test").addEventListener("click", function() { console.log("clicked:", this); }); </script> </body><!-- closing body tag -->

虽然这很有意义,而且对于传统浏览器来说是一个可靠的选择,但它是有限的,而且还有更灵活、更现代的方法可用。


选项2:defer属性

虽然我们确实说过脚本“(通常)在遇到时执行”,但现代浏览器允许您指定不同的行为。如果要链接外部脚本,可以使用defer属性。

[defer,一个布尔属性,]被设置为向浏览器指示脚本将在文档解析之后,但在触发DOMContentLoaded之前执行。

这意味着您可以将标记为defer的脚本放置在任何地方,甚至是<head>,并且它应该可以访问完全实现的DOM。

<script src=“https://gh-canon.github.io/misc-demos/log-test-click.js” defer></script> <按钮 id=“测试”>单击我</button>

只要记住…

Defer只能用于外部脚本,即:那些具有SRC属性的脚本。 注意浏览器的支持,例如:IE中有bug的实现< 10


选项3:模块

根据您的需求,您可以使用JavaScript模块。在与标准脚本的其他重要区别中(这里提到了),模块是自动延迟的,并且不限于外部源。

将脚本的类型设置为模块,例如:

<脚本类型=“模块”> . getelementbyid(“测试”)。addEventListener(“点击”,函数(e) { Console.log("点击:",这个); }); > < /脚本 <button id="test">click me</button>


选项4:延迟事件处理

向解析文档后触发的事件添加侦听器。

DOMContentLoaded事件内

DOMContentLoaded在从初始解析完全构建完DOM之后触发,而不需要等待样式表或图像之类的东西加载。

<script> document.addEventListener(“DOMContentLoaded”, function(e){ document.getElementById(“test”).addEventListener(“click”, function(e) { console.log(“点击:”,这个); }); }); </script> <按钮 id=“测试”>单击我</button>

窗口:加载事件

加载事件在加载DOMContentLoaded和其他资源(如样式表和图像)之后触发。由于这个原因,它的发射时间比我们预期的要晚。不过,如果你考虑的是像IE8这样的老浏览器,这种支持几乎是通用的。当然,您可能需要addEventListener()的polyfill。

<脚本> 窗口。addEventListener(“负载”,函数(e) { . getelementbyid(“测试”)。addEventListener(“点击”,函数(e) { console.log(“点击:“,这个); }); }); > < /脚本 <button id="test">click me</button>

jQuery的准备()

DOMContentLoaded和window:load都有各自的注意事项。jQuery的ready()提供了一个混合的解决方案,在可能的时候使用DOMContentLoaded,在必要的时候切换到window:load,并且在DOM已经完成的时候立即触发它的回调。

你可以把你的现成处理程序直接传递给jQuery作为$(handler),例如:

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> . <脚本> $(函数(){ $(" #测试”).click(函数(){ console.log(“点击:“,这个); }); }); > < /脚本 <button id="test">click me</button>


选项5:事件委托

将事件处理委托给目标元素的祖先。

当一个元素引发了一个事件(假设它是一个冒泡事件,并且没有任何东西阻止它的传播),该元素祖先中的每个父元素,一直到窗口,也会接收到该事件。这允许我们将一个处理程序附加到一个现有的元素上,并在它们从它的后代中冒泡时对事件进行采样……甚至来自附加处理程序后添加的后代。我们所要做的就是检查事件,看它是否由所需的元素引发,如果是,则运行我们的代码。

通常,这种模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,选择目标元素最近的可靠祖先,而不是将其附加到文档。

原生JavaScript

< div id =“祖先”> < !——我们的脚本可用的最近的祖先——> <脚本> . getelementbyid(“祖先”)。addEventListener(“点击”,函数(e) { 如果(e。target。Id === "后代"){ console.log(“点击:“e。target); } }); > < /脚本 <button id="后人">click me</button> . < / div >

jQuery是()

jQuery通过on()实现了这个功能。给定一个事件名称,一个所需后代的选择器和一个事件处理程序,它将解析您委托的事件处理并管理您的this上下文:

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> . < div id =“祖先”> < !——我们的脚本可用的最近的祖先——> <脚本> $(" #祖先”)。On ("click", "#后人",函数(e) { console.log(“点击:“,这个); }); > < /脚本 <button id="后人">click me</button> . < / div >


简单地说:因为您要查找的元素(目前)还不存在于文档中。


对于这个答案的其余部分,我将使用getElementById作为示例,但同样适用于getElementsByTagName、querySelector和任何其他选择元素的DOM方法。

可能的原因

一个元素不存在的原因有三个:

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 (like querySelector), 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 # with getElementById, and must use it with querySelector 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 using querySelector (document.querySelector("#the\\.id"))) but not when using getElementById (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 in iframes aren't searched when you search the document that contains them.

如果问题是原因3(它在iframe或类似文件中),则需要查看iframe中的文档,而不是父文档,方法可能是获取iframe元素并使用其contentDocument属性访问其文档(仅同源)。这个答案的其余部分解决了前两个原因。

第二个原因——目前还没有——很常见。浏览器从上到下解析和处理HTML。这意味着在HTML中出现DOM元素之前对DOM元素的任何调用都将失败。

考虑下面的例子:

<script>
    var element = document.getElementById('my_element');
</script>

<div id="my_element"></div>

div出现在脚本之后。在脚本执行时,元素还不存在,getElementById将返回null。

jQuery

这同样适用于jQuery的所有选择器。如果你的选择器拼写错误,或者你试图在元素实际存在之前选择它们,jQuery将无法找到它们。

一个额外的扭曲是,当你没有找到jQuery,因为你已经加载了脚本没有协议,并从文件系统运行:

<script src="//somecdn.somewhere.com/jquery.min.js"></script>

此语法用于允许脚本在协议为https://的页面上通过HTTPS加载,并在协议为http://的页面上加载HTTP版本

它有一个不幸的副作用,试图加载文件失败://somecdn.somewhere.com…


解决方案

在调用getElementById(或任何与此相关的DOM方法)之前,确保要访问的元素存在,即DOM已加载。

只需将JavaScript放在相应的DOM元素之后就可以确保这一点

<div id="my_element"></div>

<script>
    var element = document.getElementById('my_element');
</script>

在这种情况下,你也可以把代码放在body结束标记(</body>)之前(所有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:方案,调整路径等)。

监听load/DOMContentLoaded事件正是jQuery使用.ready() [docs]所做的事情。所有影响DOM元素的jQuery代码都应该在该事件处理程序中。

事实上,jQuery教程明确指出:

当使用jQuery时,我们所做的几乎所有事情都是读取或操作文档对象模型(DOM),我们需要确保一旦DOM准备好,我们就开始添加事件等。

为此,我们为文档注册一个就绪事件。 $(文档)时函数(){

   // do stuff when DOM is ready
});

或者你也可以使用简写语法:

$(function() {
    // do stuff when DOM is ready
});

两者是等价的。


正如@FelixKling指出的那样,最有可能的情况是您正在寻找的节点(目前)还不存在。

然而,现代开发实践通常可以在文档树之外操作文档元素,可以使用DocumentFragments,也可以直接分离/重新附加当前元素。这样的技术可以作为JavaScript模板的一部分,或者在有问题的元素被大量修改时避免过度的重绘/回流操作。

类似地,在现代浏览器中推出的新的“Shadow DOM”功能允许元素成为文档的一部分,但不能按文档查询。getElementById及其所有兄弟方法(querySelector等)。这样做是为了封装功能并具体地隐藏它。

但是,您所寻找的元素很可能(还)不在文档中,您应该按照Felix的建议去做。但是,您也应该意识到,这越来越不是一个元素可能无法找到(无论是暂时的还是永久的)的唯一原因。


基于id的选择器不能工作的原因

指定id的元素/DOM还不存在。 元素存在,但它没有在DOM中注册[如果从Ajax响应动态追加HTML节点]。 存在多个具有相同id的元素,这将导致冲突。

解决方案

尝试在元素声明之后访问它,或者使用类似于$(document).ready()的东西; 对于来自Ajax响应的元素,使用jQuery的.bind()方法。旧版本的jQuery使用.live()来实现同样的功能。 使用工具[例如,浏览器的webdeveloper插件]找到重复的id并删除它们。


如果您试图访问的元素在iframe内部,而您试图在iframe上下文之外访问它,这也会导致它失败。

如果你想在iframe中获取一个元素,你可以在这里找到方法。


如果脚本执行顺序不是问题,另一个可能的原因是元素没有被正确选择:

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: <div id="foo"></div> // Error, selected element will be null: document.getElementById('#foo') // Fix: document.getElementById('foo') Similarly, for getElementsByClassName, don't prefix the passed string with a .: <div class="bar"></div> // Error, selected element will be undefined: document.getElementsByClassName('.bar')[0] // Fix: document.getElementsByClassName('bar')[0] 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: <div class="baz"></div> // Error, selected element will be null: document.querySelector('baz') $('baz') // Fix: document.querySelector('.baz') $('.baz') 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: <div class="foo bar"></div> use the query string .foo.bar. To select <div class="foo" data-bar="someData"></div> use the query string .foo[data-bar="someData"]. To select the <span> below: <div class="parent"> <span data-username="bob"></span> </div> 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: <div class="result"></div> // Error, selected element will be null: document.querySelector('.results') $('.Result') // Fix: document.querySelector('.result') $('.result') You also need to make sure the methods have the proper capitalization and spelling. Use one of: $(selector) document.querySelector document.querySelectorAll document.getElementsByClassName document.getElementsByTagName document.getElementById 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.