HTML页面的生命周期
页面生命周期:DOMContentLoaded, load, beforeunload, unload
HTML
页面的生命周期有以下三个重要事件:
DOMContentLoaded
— 浏览器已经完全加载了HTML
,DOM
树已经构建完毕,但是像是<img>
和样式表等外部资源可能并没有下载完毕。load
— 浏览器已经加载了所有的资源(图像,样式表等)。beforeunload/unload
– 当用户离开页面的时候触发。
每个事件都有特定的用途
DOMContentLoaded
–DOM
加载完毕,所以js可以访问所有DOM
节点,初始化界面。load
– 附加资源已经加载完毕,可以在此事件触发时获得图像的大小(如果没有被在HTML/CSS
中指定)beforeunload/unload
– 用户正在离开页面:可以询问用户是否保存了更改以及是否确定要离开页面。
来看一下每个事件的细节。
DOMContentLoaded
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 0x0alert(`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
树构建完毕之后就运行该事件,不过其实存在一些陷阱。
DOMContentLoaded
和脚本
当浏览器在解析HTML
页面时遇到了 <script>...</script>
标签,将无法继续构建DOM
树(译注:UI
渲染线程与JS
引擎是互斥的,当JS
引擎执行时UI
线程会被挂起),必须立即执行脚本。所以 DOMContentLoaded
有可能在所有脚本执行完毕后触发。
外部脚本(带src
的)的加载和解析也会暂停DOM
树构建,所以 DOMContentLoaded
也会等待外部脚本。
不过有两个例外是带async
和defer
的外部脚本,他们告诉浏览器继续解析而不需要等待脚本的执行,所以用户可以在脚本加载完成前可以看到页面,有较好的用户体验。
async
和defer
属性仅仅对外部脚本起作用,并且他们在src
不存在时会被自动忽略。
它们都告诉浏览器继续处理页面上的内容,而在后台加载脚本,然后在脚本加载完毕后再执行。所以脚本不会阻塞DOM
树的构建和页面的渲染。
(译注:其实这里是不对的,带有async
和defer
的脚本的下载是和HTML
的下载与解析是异步的,但是js
的执行一定是和UI线程是互斥的,像下面这张图所示,async
在下载完毕后的执行会阻塞HTML
的解析)
他们有两处不同:
async | defer | |
---|---|---|
顺序 | 带有async的脚本是优先执行先加载完的脚本,他们在页面中的顺序并不影响他们执行的顺序。 | 带有defer的脚本按照他们在页面中出现的顺序依次执行。 |
DOMContentLoaded | 带有async的脚本也许会在页面没有完全下载完之前就加载,这种情况会在脚本很小或本缓存,并且页面很大的情况下发生。 | 带有defer的脚本会在页面加载和解析完毕后执行,刚好在DOMContentLoaded之前执行。 |
所以async
用在那些完全不依赖其他脚本的脚本上。
DOMContentLoaded
与样式表
外部样式表并不会影响DOM,所以DOMContentLoaded
并不会被他们阻塞。
不过仍然有一个陷阱:如果在样式后面有一个内联脚本,那么脚本必须等待样式先加载完。
(译注:简单来说,JS 因为有可能会去获取 DOM
的样式,所以 JS 会等待样式表加载完毕,而 JS 是阻塞 DOM
的解析的,所以在有外部样式表的时候,JS 会一直阻塞到外部样式表下载完毕)
<link type="text/css" rel="stylesheet" href="style.css">
<script>// the script doesn't not execute until the stylesheet is loaded// 脚本直到样式表加载完毕后才会执行。alert(getComputedStyle(document.body).marginTop);
</script>
发生这种事的原因是脚本也许会像上面的例子中所示,去得到一些元素的坐标或者基于样式的属性。所以他们自然要等到样式加载完毕才可以执行。
DOMContentLoaded
需要等待脚本的执行,脚本又需要等待样式的加载。
浏览器的自动补全
Firefox, Chrome和Opera会在DOMContentLoaded
执行时自动补全表单。
例如,如果页面有登录的界面,浏览器记住了该页面的用户名和密码,那么在 DOMContentLoaded
运行的时候浏览器会试图自动补全表单(如果用户设置允许)。
所以如果DOMContentLoaded
被一个需要长时间执行的脚本阻塞,那么自动补全也会等待。你也许见过某些网站(如果你的浏览器开启了自动补全)—— 浏览器并不会立刻补全登录项,而是等到整个页面加载完毕后才填充。这就是因为在等待DOMContentLoaded
事件。
使用带async
和defer
的脚本的一个好处就是,他们不会阻塞DOMContentLoaded
和浏览器自动补全。(译注:其实执行还是会阻塞的)
2018.02.05:defer
是会阻塞 DOMContentLoaded
的,被 defer
的脚本要在DOMContentLoaded
触发前执行,所以如果HTML
很快就加载完了(先不考虑 CSS
阻塞 DOMContentLoaded
的情况),而 defer
的脚本还没有加载完,浏览器就会等,等到脚本加载完,执行完,再触发 DOMContentLoaded
,放上一张图(取自在 devTool
下分析自己写的一个页面)
可以看到,
HTML
很快就加载和解析完毕(CSS
在这里是动态加载的,不阻塞 DOMContentLoaded
),jQuery
和main.js
的脚本是 defer
的, DOMContentLoaded
(蓝线)一直在等,等到这两个脚本下载完并执行完,才触发了 DOMContentLoaded
。
从这个角度看来,defer
和把脚本放在 </body>
前真是没啥区别,只不过 defer
脚本位 于head
中,更早被读到,加载更早,而且不担心会被其他的脚本推迟下载开始的时间。
window.onload
window
对象上的onload
事件在所有文件包括样式表,图片和其他资源下载完毕后触发。
下面的例子正确检测了图片的大小,因为window.onload
会等待所有图片的加载。
<script>window.onload = function() {alert('Page loaded');// image is loaded at this timealert(`Image size:${img.offsetWidth}x${img.offsetHeight}`);};</script><img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
window.onunload
用户离开页面的时候,window
对象上的unload
事件会被触发,我们可以做一些不存在延迟的事情,比如关闭弹出的窗口,可是我们无法阻止用户转移到另一个页面上。
所以我们需要使用另一个事件 — onbeforeunload
。
window.onbeforeunload
如果用户即将离开页面或者关闭窗口时,beforeunload
事件将会被触发以进行额外的确认。
浏览器将显示返回的字符串,举个例子:
window.onbeforeunload = function() {return "There are unsaved changes. Leave now?";
};
有些浏览器像Chrome
和火狐会忽略返回的字符串取而代之显示浏览器自身的文本,这是为了安全考虑,来保证用户不受到错误信息的误导。
readyState
如果我们在整个页面加载完毕后设置DOMContentLoaded
会发生什么呢?
啥也没有,DOMContentLoaded
不会被触发。
有一些情况我们无法确定页面上是否已经加载完毕,比如一个带有async
的外部脚本的加载和执行是异步的(注:执行并不是异步的-_-)。在不同的网络状况下,脚本有可能是在页面加载完毕后执行也有可能是在页面加载完毕前执行,我们无法确定。所以我们需要知道页面加载的状况。
document.readyState
属性给了我们加载的信息,有三个可能的值:
loading
加载 -document
仍在加载。interactive
互动 - 文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。complete
- 文档和所有子资源已完成加载。状态表示load
事件即将被触发。
所以我们可以检查 document.readyState
的状态,如果没有就绪可以选择挂载事件,如果已经就绪了就可以直接立即执行。
像这样:
function work() { /*...*/ }if (document.readyState == 'loading') {document.addEventListener('DOMContentLoaded', work);
} else {work();
}
每当文档的加载状态改变的时候就有一个readystatechange
事件被触发,所以我们可以打印所有的状态。
// current state
console.log(document.readyState);// print state changes
document.addEventListener('readystatechange', () => console.log(document.readyState));
readystatechange
是追踪页面加载的一个可选的方法,很早之前就已经出现了。不过现在很少被使用了,为了保持完整性还是介绍一下它。
readystatechange
的在各个事件中的执行顺序又是如何呢?
<script>function log(text) { /* output the time and message */ }log('initial readyState:' + document.readyState);document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));window.onload = () => log('window onload');</script><iframe src="iframe.html" onload="log('iframe onload')"></iframe><img src="http://en.js.cx/clipart/train.gif" id="img">
<script>img.onload = () => log('img onload');</script>
输出如下:
[1] initial readyState:loading[2] readyState:interactive[2] DOMContentLoaded[3] iframe onload[4] readyState:complete[4] img onload[4] window onload
方括号中的数字表示他们发生的时间,真实的发生时间会更晚一点,不过相同数字的时间可以认为是在同一时刻被按顺序触发(误差在几毫秒之内)
document.readyState
在DOMContentLoaded
前一刻变为interactive
,这两个事件可以认为是同时发生。document.readyState
在所有资源加载完毕后(包括iframe
和img
)变成complete
,我们可以看到complete
、img.onload
和window.onload
几乎同时发生,区别就是window.onload
在所有其他的load
事件之后执行。
总结
页面事件的生命周期:
DOMContentLoaded
事件在DOM
树构建完毕后被触发,我们可以在这个阶段使用js去访问元素。async
和defer
的脚本可能还没有执行。- 图片及其他资源文件可能还在下载中。
load
事件在页面所有资源被加载完毕后触发,通常我们不会用到这个事件,因为我们不需要等那么久。beforeunload
在用户即将离开页面时触发,它返回一个字符串,浏览器会向用户展示并询问这个字符串以确定是否离开。unload
在用户已经离开时触发,我们在这个阶段仅可以做一些没有延迟的操作,由于种种限制,很少被使用。document.readyState
表征页面的加载状态,可以在readystatechange
中追踪页面的变化状态:loading
— 页面正在加载中。interactive
– 页面解析完毕,时间上和DOMContentLoaded
同时发生,不过顺序在它之前。complete
– 页面上的资源都已加载完毕,时间上和window.onload
同时发生,不过顺序在他之前。
HTML页面的生命周期相关推荐
- 【微信小程序企业级开发教程】页面的生命周期和参数传递
文章目录 1 页面的生命周期 2 参数传递 2.1 第一种method 2.2 第二种method 1 页面的生命周期 2 参数传递 2.1 第一种method 第一个界面代码: 要跳转界面的代码: ...
- Asp.net2.0页面的生命周期(续)
以上就是Asp.net页面生命周期中的几个主要事件.每次我们请求一个Asp.net页面时,我们都经历着同样的过程:从初始化对象到销毁对象.通过了解Asp.net页面的内部运行机制,我相信大家在编写.调 ...
- 【转】Asp.net页面的生命周期
介绍 Asp.net是微软.Net战略的一个组成部分.它相对以前的Asp有了很大的发展,引入了许多的新机制.本文就Asp.net页面的生命周期向大家做一个初步的介绍,以期能起到指导大家更好.更灵活地操 ...
- .Net页面的生命周期(ZZ)
1. 初始化:主要是执行Page的Init事件和OnIint方法. 2. 加载视图状态:主要是执行LoadViewState方法,就是从ViewState中获取上一次的状态 ...
- WEB页面的生命周期,DOMContentLoaded,load,beforeunload,unload
简言 理解WEB页面的生命周期,文档加载事件及顺序对WEB开发有十分的重要意义.如果不理解,在元素未加载就提前操作元素,则得不到想要的结果.而如果页面完全加载完成后,再进行操作,则又会影响用户体验. ...
- ZT Web Control 开发系列(一) 页面的生命周期
http://www.cnblogs.com/joeliu/category/143125.html Page是WebForm编程基本元素,它从TemplateControl派生,而TemplateC ...
- Asp.net2.0页面的生命周期
当一个获取网页的请求(可能是通过用户提 交完成的,也可能是通过超链接完成的)被发送到Web服务器后,这个页面就会接着运行从创建到处理完成的一系列事件.在我们试图建立Asp.net页面的 时候,这个执行 ...
- HTML 页面的生命周期、HTML 事件
From:https://blog.csdn.net/WuLex/article/details/101016936 1.页面生命周期 HTML页面的生命周期有以下三个重要事件,每个事件都有特定的用途 ...
- 微信小程序详解——小程序的生命周期和页面的生命周期
我是一名安卓程序员,我们安卓中最明显的特征就是类具有生命周期.所以当开发小程序的时候,我自然而然的会先研究小程序的生命周期.在Android中是通过Application来管理安卓程序的生命周期,小程 ...
- uni-app 页面组件生命周期
不论是app还是小程序,生命周期是非常重要的知识点. uni-app 支持如下生命周期函数: 页面的生命周期 onLoad 监听页面加载,其参数为上个页面传递的数据,参数类型为object(用于页面传 ...
最新文章
- “数学不好,干啥都不行!”资深程序员:别再瞎努力了!
- vue 2.6 中 slot 的新用法
- Dalvik/ART(ANDROID)中的多线程机制(4)
- 使用ViewContainerRef探索Angular DOM操作技术
- python数据类型及字符编码
- 一些好用的nginx第三方模块
- scanf 与 scanf_s
- mysql最简单的查看_查看Mysql版本号 (最简单的是status )
- Linux上,最常用的一批命令解析
- 初级产品经理的日常工作流程汇总
- 神经网络模型的工作原理,神经网络模型数据处理
- 一文彻底搞懂激光雷达原理!
- 各层电子数排布规则_核外电子的排布及其规律,亨利·莫塞莱和查尔斯·巴克拉最先发现电子层...
- 39 What Determines the Kind of Person You Are ?是什么决定了你是哪种内型的人 ?
- SpringBoot优缺点分析
- Python|制作汉堡的解题方法
- 【...】12306官网购买指定铺位的车票
- Docker部署若伊前后端分离项目到阿里云服务器
- SBUF, TI/RI, ES
- 为何国外的人都爱用电子邮箱?注册电子邮箱有哪些好处呢
热门文章
- 数据库连接中OleDbConnection的用法
- oledbconnection java_如何使用C#和OleDbConnection读取.xlsx和.xls文件?
- 什么样的男人才是女人眼中最帅的男人
- 星球企划书 | 从这个星球赚取你的第一桶金
- 桌面上 计算机 回收站不见了怎么办,电脑回收站不见了怎么办 四种方法教你快速解决问题【图文教程】...
- Glide之Target
- WIFI模块AT指令配置模块连接路由向公网发送数据(花生壳内网穿墙)
- 【学步者日记】C#反射中NonPublic和Instance需要一起使用
- 黑马博客实战项目中 Mongoose 错误-RangeError: Maximum call stack size exceeded,返回的文档过大导致模板引擎无法渲染的问题
- AI工程化,让人工智能回归现实