这篇文章上次修改于 551 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
你曾在编写原生 JS 或 JQuery 代码的时候,是不是会经常遇到 DOM 元素获取不到的问题?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="your-script.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
// your-script.js
document.getElementById("app").innerHTML = "I am Paul.";
Uncaught TypeError: Cannot set property 'innerHTML' of null
at your-script.js:1
为什么
这是因为你的代码在页面加载完成之前就已经被执行了,默认只能获取到 script
标签声明之前的内容。所以可以将 script
标签放在最后面,是平常最简单直接的办法,防止拿不到对应的 DOM 元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="your-script.js"></script>
</body>
</html>
但在某些情况下,script
标签不一定能被放在最后面,于是需要考虑到如何在页面内容加载完成后再执行代码。写过 JQuery 的同学,一定都知道 $(document).ready()
这个 API。
// JQuery
$(document).ready(function() {
$("#app").html("I am Paul").
})
或者是原生版本:
// Vanilla
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("app").innerHTML = "I am Paul.";
})
// ! 不推荐,因为会因阻塞资源(图片等)导致长时间不会执行,就是页面所有资源加载完成之后才会执行
window.onload = function () {
document.getElementById("app").innerHTML = "I am Paul.";
}
这里面基本上都是一个「匿名函数」封装了一波,以此实现让浏览器加载完页面内容之后才执行 script
的内容。
defer,新的帮手
然而,有一个你也许并不知道的属性,非常完美的解决这个问题。
<script src="your-script.js" defer></script>
没错,只需要在 script
标签上加一个 defer
属性,就可以让浏览器知道这个脚本需要在页面内容加载完成(不包括 onload 提到的资源阻塞)之后才执行了。完全不再需要把脚本放在最底部,或者是设置以上两个事件,就可以使用。
your-script.js
现在只需要一行代码直接执行,就可以拿到 DOM 上的内容了。
document.getElementById("app").innerHTML = "I am Paul.";
当然,相信大家并不会这样做,毕竟这样写会把变量完全公开出来,很容易被控制台获取,编写一个脚本重写代码更是「轻而易举」。实际情况去编写一段简单的代码,依然需要匿名函数的封装。
(function () {
document.getElementById("app").innerHTML = "I am Paul.";
})();
再举个例子,Svelte 框架生成的原生代码是这样的:
var app = function () {
...
}();
兼容性
这个你就不用担心了,连 IE10 都能用,其他浏览器基本上都是 OK 的。
执行顺序
全部带上 defer
的 script
标签,将和一般引入的情况执行顺序相同。
<script src="kico.js" defer></script>
<script src="your-script.js" defer></script>
如果一个带上,一个不带上,执行顺序就是先执行不带 defer
的,再执行带 defer
的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- (2) 再执行它 -->
<script src="your-script.js" defer></script>
</head>
<body>
<div id="app"></div>
<!-- (1) 先执行 -->
<script src="your-script-2.js"></script>
</body>
</html>
已有 2 条评论
大佬牛批哇
感谢大佬分享,平时还真没注意到这个属性