document.getElementById
,$("#id")
或任何其他DOM方法/ jQuery选择器找不到元素的可能原因是什么?
示例问题包括:
.val()
,.html()
,.text()
)返回undefined
null
的标准DOM方法会导致以下几种错误:未捕获的TypeError:无法设置为null的属性" ..."未被捕获的TypeError:无法读取为null的属性" ..."
最常见的形式是:
未捕获的TypeError:无法将属性'onclick'设置为null
未捕获的TypeError:无法读取null的属性'addEventListener'
未捕获的TypeError:无法读取null的属性"样式"
您要查找的元素不在 DOM < / a>脚本运行时。
依赖DOM的脚本的位置可能对其行为产生深远的影响。浏览器从上到下解析HTML文档。将元素添加到DOM中,并在遇到脚本时(通常)执行脚本。 这意味着顺序很重要。通常,脚本找不到在标记中稍后出现的元素,因为这些元素尚未添加到DOM。
考虑以下标记;脚本1成功执行后,脚本1无法找到
<script>
console.log("script #1: %o", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
console.log("script #2: %o", document.getElementById("test")); // <div id="test" ...
</script>
那么,您应该怎么办?您有几种选择:
将脚本移动到页面的更下方,紧接在结束body标签之前。以这种方式组织的文档的其余部分在执行脚本之前已被解析:
<body>
<button id="test">click me</button>
<script>
document.getElementById("test").addEventListener("click", function() {
console.log("clicked: %o", this);
});
</script>
</body><!-- closing body tag -->
ready()
使用 ready()
<,将您的脚本推迟到DOM被完全解析为止/ a>:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("#test").click(function() {
console.log("clicked: %o", this);
});
});
</script>
<button id="test">click me</button>
注意:您可以简单地绑定到 DOMContentLoaded
或window.
,但每个都有其警告。 jQuery的 ready()
提供了一种混合解决方案。
委派的事件的优点是,它们可以处理来自后继元素添加到文档中。
当元素引发事件时(假设它是冒泡事件,并且没有任何事件能够阻止其传播),该元素祖先中的每个父对象也会收到该事件。这使我们可以将处理程序附加到现有元素,并在事件从其后代冒泡时进行采样(甚至是附加了处理程序之后添加的事件)。我们要做的就是检查该事件以查看它是否由所需的元素引发,如果是,则运行我们的代码。
jQuery的 on()
为我们执行了该逻辑。我们只需提供一个事件名称,所需后代的选择器和事件处理程序:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).on("click", "#test", function(e) {
console.log("clicked: %o", this);
});
</script>
<button id="test">click me</button>
注意:通常,此模式保留给加载时或不存在的元素,以避免附加大量处理程序。值得指出的是,虽然我已经在document
上附加了一个处理程序(出于演示目的),但是您应该选择最近的可靠祖先。
defer
属性使用 defer
的code> 属性。
[
defer
,一个布尔属性,]设置为向浏览器指示脚本应在文档解析后但在触发DOMContentLoaded
。
作为参考,这是来自外部脚本的代码:
注意:defer
属性肯定似乎像魔术子弹,但是重要的是要注意警告...
1. defer
仅可用于外部脚本,即:具有src
属性的脚本。
2.注意浏览器支持,即:IE <10
简短:因为您要查找的元素尚未存在于文档中。
对于此答案的其余部分,我将使用getElementById
作为示例,但对 getElementsByTagName
, querySelector
和任何其他选择元素的DOM方法。
可能的原因
元素可能不存在的原因有两个:
文档中实际上不存在带有传递ID的元素。您应仔细检查传递给getElementById
的ID是否与(生成的)HTML中现有元素的ID真正匹配,以及您没有拼写错误(该ID为区分大小写!)。
您调用getElementById
的元素暂时不存在 。
后一种情况很常见。浏览器从上到下解析并处理HTML。这意味着在DOM元素出现在HTML中之前对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版本。它有尝试并无法加载file://somecdn.somewhere.com...
解决方案
在调用getElementById
(或与此相关的任何DOM方法)之前,请确保要访问的元素存在,即DOM已加载。
只需将JavaScript 放在相应的DOM元素之后,即可确保这一点
<div id="my_element"></div>
<script>
var element = document.getElementById('my_element');
</script>
在这种情况下,您也可以将代码放在结束正文标记()之前(在执行脚本时所有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文件并更正了网址(如果不是)(例如,在开头添加http:
或https:
方案,调整路径等)
收听load
/ DOMContentLoaded
事件正是jQuery使用 .ready()
[docs] 。您所有影响DOM元素的jQuery代码都应在该事件处理程序内。
实际上, jQuery教程明确指出:
几乎所有使用jQuery进行的操作都会读取或操作文档对象模型(DOM),因此我们需要确保在DOM准备就绪后立即开始添加事件等。
为此,我们为文档注册了一个就绪事件。
$(document).ready(function() { // do stuff when DOM is ready });
或者,您也可以使用简写语法:
$(function() {
// do stuff when DOM is ready
});
两者都是等效的。
基于ID的选择器不起作用的原因
解决方案
尝试在元素声明后访问元素,或者使用诸如$(document).ready();
对于来自Ajax响应的元素,请使用jQuery的.bind()
方法。较旧版本的jQuery具有相同的.live()
。
使用工具(例如,用于浏览器的webdeveloper插件)查找重复的ID并将其删除。
正如@FelixKling指出的那样,最可能的情况是您要查找的节点不存在(尚未)。
但是,现代开发实践通常可以使用DocumentFragments或直接直接分离/重新附加当前元素来操纵文档树之外的文档元素。这样的技术可以用作JavaScript模板的一部分,或避免在所涉及的元素发生重大更改时避免过多的重画/重排操作。
类似地,跨现代浏览器推出的新" Shadow DOM"功能允许元素成为文档的一部分,但不能由document.getElementById及其所有同级方法(querySelector等)查询。这样做是为了封装功能并专门隐藏它。
不过,很可能您正在寻找的元素很简单,但尚未在文档中,您应该按照Felix的建议进行操作。但是,您还应该意识到,这不再是元素可能无法找到(临时或永久)的唯一原因。
如果不是脚本执行顺序的问题,则此问题的另一个可能原因是未正确选择元素:
getElementById
要求传递的字符串是ID verbatim ,而没有别的。如果您为传递的字符串加上#
前缀,并且ID并非以#
开头,则不会选择任何内容:
<div id="foo"></div>
// Error, selected element will be null:
document.getElementById('#foo')
// Fix:
document.getElementById('foo')
类似地,对于getElementsByClassName
,不要在传递的字符串前加上前缀.
:
<div class="bar"></div>
// Error, selected element will be undefined:
document.getElementsByClassName('.bar')[0]
// Fix:
document.getElementsByClassName('bar')[0]
使用querySelector,querySelectorAll和jQuery,要与具有特定类名的元素匹配,请在类之前直接放置一个.
。同样,要匹配具有特定ID的元素,请将#
直接放在ID之前:
<div class="baz"></div>
// Error, selected element will be null:
document.querySelector('baz')
$('baz')
// Fix:
document.querySelector('.baz')
$('.baz')
此处的规则在大多数情况下与CSS选择器相同,可以在此处。
要匹配具有两个或更多属性(例如两个类名,或一个类名和一个data-
属性)的元素,请将每个属性的选择器彼此相邻在选择器字符串中,没有用空格隔开(因为空格表示后代选择器)。例如,要选择:
<div class="foo bar"></div>
,请使用查询字符串.foo.bar
。要选择
<div class="foo" data-bar="someData"></div>
,请使用查询字符串.foo[data-bar="someData"]
。要在下面选择:
<div class="parent">
<span data-username="bob"></span>
</div>
使用div.parent>span[data-username="bob"]
。
大写和拼写至关重要。如果大小写不同或拼写不同,则不会选择该元素:
<div class="result"></div>
// Error, selected element will be null:
document.querySelector('.results')
$('.Result')
// Fix:
document.querySelector('.result')
$('.result')
您还需要确保方法具有正确的大小写和拼写。使用以下之一:
$(selector)
document.querySelector
document.querySelectorAll
document.getElementsByClassName
document.getElementsByTagName
document.getElementById
任何其他拼写或大写字母均无效。例如,document.getElementByClassName
将引发错误。
确保将字符串传递给这些选择器方法。如果您将不是字符串的内容传递给querySelector
,getElementById
等,则几乎肯定不会起作用。
问题是执行javascript代码时未创建dom元素" speclist"。因此,我将javascript代码放在一个函数中,并在body onload事件中调用了该函数。
function do_this_first(){
//appending code
}
<body onload="do_this_first()">
</body>
我的解决方案是不使用锚元素的ID:
。显然,blazor和其他spa框架的问题跳到了同一页面上。为了解决这个问题,我必须使用document.getElementById('star_wars')
。但是,直到我将id放入段落元素中后,此方法才起作用:
。
使用引导程序的示例:
<button class="btn btn-link" onclick="document.getElementById('star_wars').scrollIntoView({behavior:'smooth'})">Star Wars</button>
... lots of other text
<p id="star_wars">Star Wars is an American epic...</p>