HTML页面的生命周期有以下三个重要事件:
-
DOMContentLoaded:浏览器已经完全加载了HTML,DOM树已经构建完毕,但是像是img和样式表等外部资源可能并没有下载完毕 -
load:浏览器已经加载了所有的资源(图像,样式表等) -
beforeunload/unload:当用户离开页面的时候触发
来看一下每个事件的细节:
1.DOMContentLoaded
(1)DOMContentLoaded由document对象触发
我们使用addEventListener来监听它:
document.addEventListener('DOMContentLoaded', ready);
举个栗子
<script>
function ready() {
alert('DOM is ready');
// image is not yet loaded (unless was cached), so the size is 0x0
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
}
document.addEventListener('DOMContentLoaded', ready);
</script>
<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
在这个例子中DOMContentLoaded在document加载完成后就被触发,无需等待其他资源的载入,所以alert输出的图像的大小为0。这么看来DOMContentLoaded似乎很简单,DOM树构建完毕之后就运行该事件,不过其实存在一些陷阱。
(2)DOMContentLoaded和脚本
当浏览器在解析HTML页面时遇到了 <script>...</script> 标签,将无法继续构建DOM树(UI渲染线程与JS引擎是互斥的,当JS引擎执行时UI线程会被挂起),必须立即执行脚本。所以DOMContentLoaded有可能在所有脚本执行完毕后触发。
外部脚本(带src的)的加载和解析执行也会暂停DOM树构建,所以DOMContentLoaded也会等待外部脚本。
不过有两个例外是带async和defer的外部脚本,async和defer属性仅仅对外部脚本起作用,并且他们在src不存在时会被自动忽略。
他们有两处不同:
- 顺序:带有
async的脚本是优先执行先加载完的脚本,他们在页面中的顺序并不影响他们执行的顺序。带有defer的脚本按照他们在页面中出现的顺序依次执行。 DOMContentLoaded:带有async的脚本也许会在页面没有完全下载完之前就加载,这种情况会在脚本很小或本地缓存,并且页面很大的情况下发生。带有defer的脚本会在页面加载和解析完毕后执行,刚好在DOMContentLoaded之前执行。
(3)浏览器的自动补全
Firefox, Chrome和Opera会在DOMContentLoaded执行时自动补全表单。
例如,如果页面有登录的界面,浏览器记住了该页面的用户名和密码,那么在DOMContentLoaded运行的时候浏览器会试图自动补全表单(如果用户设置允许)。
所以如果DOMContentLoaded被一个需要长时间执行的脚本阻塞,那么自动补全也会等待。你也许见过某些网站(如果你的浏览器开启了自动补全)—— 浏览器并不会立刻补全登录项,而是等到整个页面加载完毕后才填充。这就是因为在等待DOMContentLoaded事件。
使用带async和defer的脚本的一个好处就是,他们不会阻塞DOMContentLoaded和浏览器自动补全。(译注:其实执行还是会阻塞的)
2.window.onload
window对象上的onload事件在所有文件包括样式表,图片和其他资源下载完毕后触发。下面的例子正确检测了图片的大小,因为window.onload会等待所有图片的加载。
<script>
window.onload = function() {
alert('Page loaded');
// image is loaded at this time
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
};
</script>
<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
3.window.onbeforeunload
如果用户即将离开页面或者关闭窗口时,beforeunload事件将会被触发以进行额外的确认。浏览器将显示返回的字符串,举个例子:
window.onbeforeunload = function() {
return 'There are unsaved changes, Leave now?';
};