在HTML页面的生命周期中,会触发三个重要的事件
- DOMContentLoaded: 当HTML文档已经完全加载并解析完成后触发,并且不用等待样式表、图片、子框架加载完成。
- load:当浏览器加载解析完所有资源,包括图片文件,样式表文件
- beforeunload/unload: 当用户离开页面
它们的可能适用场景:
- DOMContentLoaded: 所有DOM元素已经就绪,通过handler查询DOM节点,初始化界面
- load: 当图片加载完成,获取图片尺寸
- beforeunload/unload: 可以检查用户是否保存了变更,并且询问是否确定离开页面
探究这些事件的一些细节:
1. DOMContentLoaded与image
Hello World
当我们去加载一个这样的页面时候,DOMContentLoaded不会等待图片的加载才去触发。
2. DOMContentLoaded与script
当浏览器加载HTML时遇到script标签,会停止构建DOM而是立即加载执行script标签内容。DOMContentLoaded事件会在所有script内容全部执行完触发。
例如:
// HTML
// index.js
alert('this is the external script')
从外部加载资源的script标签,通过设置async和defer属性,可以告知浏览器不要等待script标签的加载和运行。设置defer的script脚本会先于DOMContentLoaded事件执行, 这一点上似乎和不设置defer属性是一样的情况
// HTML
设置async属性时,可能会在DOMContentLoaded事件触发之前或者之后执行
// HTML
3. DOMContentLoaded与style
外部加载的样式表不会影响构造DOM,DOMContentLoaded事件不会等待样式表的加载。
但是有一个陷阱:当link标签加载一个样式表,在它后面的那个script标签必须要等待样式表文件的加载才会执行。
Hello World
现在我们发现,DOMContentLoaded在实际场景中可能还是也需要等待样式文件的加载后才触发
4. 浏览器的自动填充
Firefox, Chrome 还有Opera的自动填充form表单都是基于DOMContentLoaded事件的。
举个例子,如果页面有一个登录表单(用户名与密码), 浏览器可能会记住密码这些值。刷新页面后,再次触发DOMContentLoaded就会完成自动填充的效果。
所以如果DOMContentLoaded事件因为script脚本的执行而延迟了一段时间才触发,那么自动填充也要延迟那么一段时间才会完成。
4. window.onload
window对象的load事件是当所有页面资源就绪后才会触发(包括外部样式表、图片、脚本等等)
4. window.onunload
当用户离开页面,window对象的unload事件就会被触发。我们可以在用户离开页面是设置一个提示,但是不能取消这次离开的操作。
因此我们需要另一个事件onbeforeunload
4. window.onbeforeunload
// HTML
Hello World
another page
通过注册beforeunload事件的处理函数,就可以让用户想离开页面的时候,弹出提示框让用户选择是否马上离开了。需要注意的在写法上:
// success
window.onbeforeunload = () => {
return "There are unsaved changes. Leave now?"
}
// fail
window.addEventListener('beforeunload', event => {
return "There are unsaved changes. Leave now?"
})
// success
window.addEventListener('beforeunload', event => {
event.returnValue = "There are unsaved changes. Leave now?"
})
对于第一种DOM0级处理程序,函数返回值不为null或者undefined都有弹出框。DOM2级的处理程序,则是event.returnValue属性的值不为null或者undefined。
另外要注意的是,像chrome或者Firefox会忽略返回的字符串而显示浏览器预设的信息。这么做是为了sheer safety(黑人问号,不知道怎么翻)。
7.readyState
如果我们在文档已经load的情况下监听DOMContentLoaded事件,并设置处理程序会怎么样?
处理程序肯定不会执行。
某些情况下,我们没法确定文档是否ready,举个例子,外部引入的script标签,设置了async属性异步执行。根据网络情况,script脚本可能会document解析的某个阶段执行,那么就有必要了解一下document.readyState
document.readyState有三个值
- 'loading': document等在加载
- 'interactive': document被完全解析
- 'complete': document被完全解析,并且所有外部资源都加载完毕
因此可以通过检查document.readyState,并且设置处理程序来完成document已经ready后的动作。
function work() { /*...*/ }
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', work);
} else {
work();
}
readystatechange事件会在document的state变化时触发,所以通过监听该事件,我们可以了解state在什么情况下变化。
具体例子
输出:
[1] initial readyState:loading
[2] readyState:interactive
[2] DOMContentLoaded
[3] iframe onload
[4] readyState:complete
[4] img onload
[4] window onload
总结起来在DOMContentLoaded之前document.readyState变成interactive;而在document.readyState变成complete之后,window.onload也随之触发。
小结:
1. 监听document对象的DOMContentLoaded的事件,会在页面所有DOM已经ready的情况下触发,我们在这个时候,开始脚本执行($(document).ready(handler))
- 所有脚本除了外部引入的有async或defer属性的部分外,都会执行。
- 图片及其他资源则继续加载 ####
2. window对象的load事件则会在页面所有资源加载完毕后触发,我们也少用到它
3. window对象的beforeunload事件,会在用户打算离开页面时触发,弹出一个对话框并包含一个问题让用户进行判断
4. window对象的unload会在用户最终离开时触发,但是似乎派不上什么用场
5. document.readyState输出当前document的state,监听readystatechange事件可以跟踪document的state。
- loading: document正在加载
- interactive: 除一些外部资源外document已经解析完成
- complete: document和外部资源全部加载完成 ####
原文连接在这儿
文中的例子在这儿