您试图查找的元素不在DOM https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model当你的脚本运行时。
依赖 DOM 的脚本的位置可能对其行为产生深远的影响。浏览器从上到下解析 HTML 文档。元素被添加到 DOM 中,并且脚本(默认情况下)在遇到它们时执行。这意味着顺序很重要。通常,脚本无法找到标记中稍后出现的元素,因为这些元素尚未添加到 DOM。
考虑以下标记;脚本 #1 找不到<div>
当脚本 #2 成功时:
<script>
console.log("script #1:", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
console.log("script #2:", document.getElementById("test")); // <div id="test" ...
</script>
那你该怎么办?您有几个选择:
选项 1:移动脚本
鉴于我们在上面的示例中看到的情况,一个直观的解决方案可能是简单地将脚本沿着标记向下移动,越过您想要访问的元素。事实上,很长一段时间,将脚本放在页面底部被认为是一种最佳实践 https://developer.yahoo.com/performance/rules.html#js_bottom由于各种原因。以这种方式组织,在执行脚本之前将解析文档的其余部分:
<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 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer属性。
[defer
,一个布尔属性,] 设置为向浏览器指示该脚本应在解析文档之后但在触发之前执行DOMContentLoaded https://developer.mozilla.org/en-US/docs/web/api/window/domcontentloaded_event.
这意味着您可以放置一个标记为defer
任何地方,甚至<head>
,并且它应该能够访问完全实现的 DOM。
<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>
请记住...
-
defer
只能用于外部脚本,即:那些具有src
属性。
- 意识到浏览器支持 https://caniuse.com/script-defer,即: IE
选项 3:模块
根据您的要求,您也许可以使用JavaScript 模块 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#applying_the_module_to_your_html。与标准脚本的其他重要区别包括(此处注明 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts),模块会自动延迟,并且不限于外部源。
设置你的脚本type
to module
, e.g.:
<script type="module">
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked: ", this);
});
</script>
<button id="test">click me</button>
选项 4:通过事件处理延迟
向解析文档后触发的事件添加侦听器。
DOMContentLoaded 事件
DOMContentLoaded https://developer.mozilla.org/en-US/docs/web/api/window/domcontentloaded_event在 DOM 从初始解析完全构建完成后触发,无需等待样式表或图像等内容加载。
<script>
document.addEventListener("DOMContentLoaded", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
窗口:加载事件
The load https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event事件在之后触发DOMContentLoaded
并且已加载样式表和图像等其他资源。因此,它的触发时间比我们预期的要晚。不过,如果您考虑使用 IE8 等较旧的浏览器,那么支持几乎是普遍的。当然,您可能想要一个填充剂用于addEventListener() https://vanillajstoolkit.com/polyfills/addeventlistener/.
<script>
window.addEventListener("load", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
jQuery 的ready()
DOMContentLoaded
and window:load
每个人都有自己的警告。 jQuery 的ready() https://api.jquery.com/ready/提供混合解决方案,使用DOMContentLoaded
如果可能的话,故障转移到window:load
必要时,如果 DOM 已经完成,则立即触发回调。
您可以将准备好的处理程序直接传递给 jQuery,如下所示$(handler)
, e.g.:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script>
$(function() {
$("#test").click(function() {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
选项 5:活动委托
将事件处理委托给目标元素的祖先。
当一个元素引发一个事件时(假设它是一个bubbling https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-bubbling事件并且没有任何东西阻止其传播),该元素祖先中的每个父代,一直到window
,也接收该事件。这允许我们将处理程序附加到现有元素,并在事件从其后代中冒出时对事件进行采样...甚至是在附加处理程序后添加的后代中。我们所要做的就是检查事件以查看它是否是由所需元素引发的,如果是,则运行我们的代码。
通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量重复的处理程序。为了提高效率,选择目标元素最近的可靠祖先,而不是将其附加到document
.
原生 JavaScript
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
document.getElementById("ancestor").addEventListener("click", function(e) {
if (e.target.id === "descendant") {
console.log("clicked:", e.target);
}
});
</script>
<button id="descendant">click me</button>
</div>
jQuery 的on()
jQuery 通过以下方式提供此功能on() https://api.jquery.com/on/。给定事件名称、所需后代的选择器和事件处理程序,它将解析您委托的事件处理并管理您的this
语境:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
$("#ancestor").on("click", "#descendant", function(e) {
console.log("clicked:", this);
});
</script>
<button id="descendant">click me</button>
</div>