PageDOMContentLoaded, load, beforeunload, unload
JavaScript Page:DOMContentLoaded, load, beforeunload, unload
Today, browsers sometimes suspend pages or abort them entirely, in case the system resources are limited. Luckily, there are modern lifecycle hooks that help to handle such interventions without affecting the user experience. (如今,浏览器有时会暂停页面或完全中止页面,以防系统资源受限。幸运的是,有现代化的生命周期挂钩,有助于在不影响用户体验的情况下处理此类干预措施。)
There are three significant events in the framework of an HTML page lifecycle:
DOMContentLoaded: the event when the browser completely loaded HTML, the DOM tree is set up, but external resources, such as pictures <img> and stylesheets might not be loaded yet.
load: the event when not only HTML is loaded, but other external resources ( for instance, images, stylesheets and so on), too.
beforeunload: the event when the user is leaving the page.
Each of the events above may be useful on a specific purpose:
DOMContentLoaded: when the DOM is ready, the handler is capable of looking up DOM nodes and initializing the interface.
load: the external resources are loaded, hence the styles are applied, the sizes of the images are known and so on.
beforeunload: the user is getting out, and it’s not possible to check if the latter has saved the changes, and ask to make sure the user really wants to leave or not.
unload: the user has almost left, yet several operations can not be initiated ( for example, sending out statistics).
Further, let’s get into the details. (此外,让我们深入了解详情。)
DOMContentLoaded
DOMContentLoaded
This event occurs on the document object. (此事件发生在文档对象上。)
The addEventListener should be used to catch it, like this:
document.addEventListener("DOMContentLoaded", ready);
// not a "document.onDOMContentLoaded = ..."
Here is an extensive example of using addEventListener:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<img id="img" src="/uploads/media/default/0001/05/e9f3899d915c17845be51e839d5ba238f0404b07.png">
<script>
function ready() {
alert('DOM is ready');
// image is not yet loaded,unless was cached, so the size is 0x0
(//图像尚未加载,除非被缓存,所以大小为0x0)
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
}
document.addEventListener("DOMContentLoaded", ready);
</script>
</body>
</html>
In the example above, the DOMContentLoaded runs once the document is loaded. So, it is capable of seeing all the elements, including <img>.
But, note that it never waits for the image to load. Hence, the alert will show zero sizes. (但是,请注意,它永远不会等待图像加载。因此,警报将显示零大小。)
The DOMContentLoaded event looks very simple at first sight. But, the event comes when the DOM tree is ready. (DOMContentLoaded事件乍看起来非常简单。但是,事件发生在DOM树准备就绪时。)
However, few peculiarities exist that we are going to cover further. (但是,我们将进一步介绍很少的特殊性。)
DOMContentLoaded and Scripts
Once the document processes an HTML-document and passes through a <script> tag, it should execute before continuing to set up the DOM. It’s like a precaution because scripts might want to modify the DOM, and, moreover, document.write into it. Therefore, the DOMContentLoaded should wait.
So, the DOMContentLoaded event occurs after scripts like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
</head>
<body>
<script>
document.addEventListener("DOMContentLoaded", () => {
alert("DOM ready!");
});
alert("Library are loaded, inline script are executed");
</script>
</body>
</html>
As you can notice from the example above, first comes “Library loaded…” , only then “DOM ready”. (您可以从上面的示例中注意到,首先是“库已加载…” ,然后是“DOM就绪”。)
Please, note that there are exceptions to this rule. That is to say, the scripts with async attribute never block DOMContentLoaded. The scripts that are created dynamically using document.createElement(‘script’) and added to the webpage after, don’t block this event, either. (请注意,此规则存在例外情况。也就是说,具有async属性的脚本从不阻止DOMContentLoaded。使用document.createElement (‘script’)动态创建并在之后添加到网页的脚本也不会阻止此事件。)
DOMContentLoaded and Styles
The DOM is not affected by external style sheets, so DOMContentLoaded doesn’t wait for them. (DOM不受外部样式表的影响,因此DOMContentLoaded不会等待它们。)
But there is a drawback here. If there is a script after the style, then the script shall wait till the stylesheet is loading, like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
</head>
<body>
<script>
// the script will not execute till the stylesheet is loaded
(//加载样式表后才会执行脚本)
alert(getComputedStyle(document.body).marginTop);
</script>
</body>
</html>
That happens because the script might want to get coordinates or other style-dependant properties. So, it should wait for the style to load. As though DOMContentLoaded waits for the scripts, it will wait for the styles before them, too. (这是因为脚本可能需要获取坐标或其他依赖于样式的属性。 因此,它应该等待样式加载。 就像DOMContentLoaded等待脚本一样,它也会等待脚本之前的样式。)
Built-in Browser Autofill
Chrome, Opera and Firefox can autofill forms on DOMContentLoaded. For example, if there is a page form with a login and password, and the browser remembered the values, then it might try to autofill them on DOMContentLoaded (it should be approved by the user). (Chrome、Opera和Firefox可以在DOMContentLoaded上自动填写表单。 例如,如果有一个带有登录名和密码的页面表单,并且浏览器记住了这些值,那么它可能会尝试在DOMContentLoaded上自动填充它们(它应该得到用户的批准)。)
Moreover, if DOMContentLoaded is suspended by the long-load scripts, the autofill should also wait. In some sites (in case of using browser autofill) the fields of login and password don’t get auto-filled at once. There is a delay until the page is completely loaded. That’s the delay till the DOMContentLoaded event. (此外,如果DOMContentLoaded被长加载脚本挂起,则自动填充也应等待。在某些网站(如果使用浏览器自动填充) ,登录名和密码字段不会同时自动填充。页面完全加载之前会有延迟。这是DOMContentLoaded事件之前的延迟。)
Window.onload
Window.onload
Now, let’s speak about the load event. This event on the window object triggers when the whole page is loaded, involving images, styles, and other resources. The load event is available via the onload property. (现在,让我们来谈谈加载事件。窗口对象上的此事件在整个页面加载时触发,涉及图像、样式和其他资源。load事件可通过onload属性获得。)
In the example below, you can see the image sizes, as window.onload waits for the overall images:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
</head>
<body>
<script>
window.onload = function() { // same as window.addEventListener('load', (event) => {
alert('Page loaded');
// image loaded at this time
(//此时已加载图片)
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
};
</script>
<img id="img" src="/uploads/media/default/0001/05/e9f3899d915c17845be51e839d5ba238f0404b07.png">
</body>
</html>
Window.onunload
Window.onunload
The unload event triggers on the window when a visitor leaves the page. You can do there something that doesn’t include a delay (for example, closing related popup window). Sending analytics is considered a notable exception. (当访问者离开页面时,卸载事件会在窗口上触发。您可以执行不包含延迟的操作(例如,关闭相关的弹出窗口)。发送分析被认为是一个值得注意的例外。)
Imagine, you want to gather data about how the page is used: scrolls, mouse clicks, and so on. As a rule, the unload event is when the user leaves the page, and you want to save the data on the server. A unique navigator.sendBeacon(url, data) method exists for such needs. It can send the data to the background. Also, there is no delay in the transition to another page still performing sendBeacon.
Here is an example of using sendBeacon:
let analyticsData = { /* object with gathered data */ };
window.addEventListener("unload", function () {
navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
};
So, in the example above:
The request is forwarded as POST. It is possible to send not only a string but also forms and other formats. There is a data limit: 64kb.
Once the sendBeacon request is over, the browser has probably left the document. Therefore, there is no way of getting server response (for analytics, it’s usually empty). (SendBeacon请求结束后,浏览器可能已离开文档。因此,无法获得服务器响应(对于分析,它通常是空的)。)
Also, you can use keepalive to perform “after-page-left” requests in the fetch method for generic network requests. (此外,您可以使用keepalive在fetch方法中为通用网络请求执行“after-page-left”请求。)
For canceling the transition to another page, you can use another event: onbeforeunload.
Window.onbeforeunload
Window.onbeforeunload
If a user has initiated navigation away from the page or intends to close the window, the beforeunload will ask for additional confirmation. In case of discarding the event, the browser will ask the user whether they are sure. See how to do it by running the following code and reloading the page, as shown below:
window.onbeforeunload = function() {
return false;
};
An interesting thing to note: returning a non-empty string also counts as aborting the event. Previously, the browsers used to show it as a message, but the modern specification doesn’t allow that.
Let’s take a look at an example:
window.onbeforeunload = function () {
return "Changes you made may not be saved. Leave site?";
};
The reason for changing the behaviour was that some webmasters abused the event handler by showing annoying messages. Still, old browsers may show messages, but there is no way of customizing the message. (更改行为的原因是一些网站管理员通过显示恼人的消息来滥用事件处理程序。尽管如此,旧的浏览器可能会显示消息,但无法自定义消息。)
ReadyState
ReadyState
The document.readyState property informs about the current loading state. (Document.readyState属性通知当前加载状态。)
Three possible values can be distinguished:
“loading”: the state of loading the document.
“interactive"“interactive”: the document is completely read.
“complete”: the document is completely read, and all the resources are loaded.
So, you can check document.readyState and set up a handler, executing the code immediately when it’s ready. (因此,您可以检查document.readyState并设置处理程序,在准备就绪时立即执行代码。)
Here is an example of using document.readyState:
function work() { /*...*/ }
if (document.readyState == 'loading') {
// loading yet, wait for the event
(//正在加载,等待事件)
document.addEventListener('DOMContentLoaded', work);
} else {
// DOM is ready!
(//DOM准备好了!)
work();
}
You can also use the readystatechange event, which gets activated when the state changes. So, all the state can be printed as follows:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<script>
// current state
(当前状态)
alert(document.readyState);
// print state changes
(//打印状态更改)
document.addEventListener('readystatechange', () => alert(document.readyState));
</script>
</body>
</html>
So, this event is an alternative way of tracking the document loading state. But, nowadays, it’s not used often. (因此,此事件是跟踪文档加载状态的替代方法。但是,现在,它不经常使用。)
The complete events flow will look like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<iframe src="iframe.html" onload="log('iframe onload')"></iframe>
<img src="/uploads/media/default/0001/05/e9f3899d915c17845be51e839d5ba238f0404b07.png" id="img">
<script>
log('initial readyState:' + document.readyState);
document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));
document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));
window.onload = () => log('window onload');
img.onload = () => log('img onload');
</script>
</body>
</html>
The example above includes <iframe>, <img>, as well as handlers for logging events.
Summary
Summary (概要)
Let’s sum up when the page load events trigger and what they can be useful for. (让我们总结一下页面加载事件何时触发以及它们的用途。)
In brief, the page load events are the following:
The DOMContentLoaded event happens on the document when the DOM is ready. So, JavaScript can be applied to elements at this stage. (-当DOM准备就绪时,文档上发生DOMContentLoaded事件。因此, JavaScript可以在此阶段应用于元素。)
The load event on the window occurs once the page and all the resources are loaded. As a rule, it’s rarely used. (-加载页面和所有资源后,窗口上发生加载事件。一般来说,它很少被使用。)
The beforeunload event is activated on the window when the user intends to leave the page. While canceling the event, the browser asks whether the user really wishes to leave. (-当用户打算离开页面时,窗口上将激活beforeunload事件。取消活动时,浏览器会询问用户是否真的希望离开。)
The unload event triggers once the user is leaving. (-用户离开后,卸载事件触发。)
And, finally, the document.readyState is the current state of the document. The changes can be tracked in the readystatechange in the following states: loading (the document is in the loading state), interactive ( the document is parsed), complete (the document and the resources are load).