99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Javascript 頁(yè)面生命周期:DOMContentLoaded,load,beforeunload,unload

2023-02-17 10:55 更新

HTML 頁(yè)面的生命周期包含三個(gè)重要事件:

  • ?DOMContentLoaded? —— 瀏覽器已完全加載 HTML,并構(gòu)建了 DOM 樹(shù),但像 ?<img>? 和樣式表之類(lèi)的外部資源可能尚未加載完成。
  • ?load? —— 瀏覽器不僅加載完成了 HTML,還加載完成了所有外部資源:圖片,樣式等。
  • ?beforeunload/unload? —— 當(dāng)用戶(hù)正在離開(kāi)頁(yè)面時(shí)。

每個(gè)事件都是有用的:

  • ?DOMContentLoaded? 事件 —— DOM 已經(jīng)就緒,因此處理程序可以查找 DOM 節(jié)點(diǎn),并初始化接口。
  • ?load? 事件 —— 外部資源已加載完成,樣式已被應(yīng)用,圖片大小也已知了。
  • ?beforeunload? 事件 —— 用戶(hù)正在離開(kāi):我們可以檢查用戶(hù)是否保存了更改,并詢(xún)問(wèn)他是否真的要離開(kāi)。
  • ?unload? 事件 —— 用戶(hù)幾乎已經(jīng)離開(kāi)了,但是我們?nèi)匀豢梢詥?dòng)一些操作,例如發(fā)送統(tǒng)計(jì)數(shù)據(jù)。

我們探索一下這些事件的細(xì)節(jié)。

DOMContentLoaded

DOMContentLoaded 事件發(fā)生在 document 對(duì)象上。

我們必須使用 addEventListener 來(lái)捕獲它:

document.addEventListener("DOMContentLoaded", ready);
// 不是 "document.onDOMContentLoaded = ..."

例如:

<script>
  function ready() {
    alert('DOM is ready');

    // 圖片目前尚未加載完成(除非已經(jīng)被緩存),所以圖片的大小為 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" rel="external nofollow"  rel="external nofollow" >

在示例中,DOMContentLoaded 處理程序在文檔加載完成后觸發(fā),所以它可以查看所有元素,包括它下面的 <img> 元素。

但是,它不會(huì)等待圖片加載。因此,alert 顯示其大小為零。

乍一看,DOMContentLoaded 事件非常簡(jiǎn)單。DOM 樹(shù)準(zhǔn)備就緒 —— 這是它的觸發(fā)條件。它并沒(méi)有什么特別之處。

DOMContentLoaded 和腳本

當(dāng)瀏覽器處理一個(gè) HTML 文檔,并在文檔中遇到 <script> 標(biāo)簽時(shí),就會(huì)在繼續(xù)構(gòu)建 DOM 之前運(yùn)行它。這是一種防范措施,因?yàn)槟_本可能想要修改 DOM,甚至對(duì)其執(zhí)行 document.write 操作,所以 DOMContentLoaded 必須等待腳本執(zhí)行結(jié)束。

因此,DOMContentLoaded 肯定在下面的這些腳本執(zhí)行結(jié)束之后發(fā)生:

<script>
  document.addEventListener("DOMContentLoaded", () => {
    alert("DOM ready!");
  });
</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js" rel="external nofollow" ></script>

<script>
  alert("Library loaded, inline script executed");
</script>

在上面這個(gè)例子中,我們首先會(huì)看到 “Library loaded…”,然后才會(huì)看到 “DOM ready!”(所有腳本都已經(jīng)執(zhí)行結(jié)束)。

不會(huì)阻塞 ?DOMContentLoaded? 的腳本

此規(guī)則有兩個(gè)例外:

  1. 具有 ?async? 特性(attribute)的腳本不會(huì)阻塞 ?DOMContentLoaded?,稍后 我們會(huì)講到。
  2. 使用 ?document.createElement('script')? 動(dòng)態(tài)生成并添加到網(wǎng)頁(yè)的腳本也不會(huì)阻塞 ?DOMContentLoaded?。

DOMContentLoaded 和樣式

外部樣式表不會(huì)影響 DOM,因此 DOMContentLoaded 不會(huì)等待它們。

但這里有一個(gè)陷阱。如果在樣式后面有一個(gè)腳本,那么該腳本必須等待樣式表加載完成:

<link type="text/css" rel="stylesheet" href="style.css">
<script>
  // 在樣式表加載完成之前,腳本都不會(huì)執(zhí)行
  alert(getComputedStyle(document.body).marginTop);
</script>

原因是,腳本可能想要獲取元素的坐標(biāo)和其他與樣式相關(guān)的屬性,如上例所示。因此,它必須等待樣式加載完成。

當(dāng) DOMContentLoaded 等待腳本時(shí),它現(xiàn)在也在等待腳本前面的樣式。

瀏覽器內(nèi)建的自動(dòng)填充

Firefox,Chrome 和 Opera 都會(huì)在 DOMContentLoaded 中自動(dòng)填充表單。

例如,如果頁(yè)面有一個(gè)帶有登錄名和密碼的表單,并且瀏覽器記住了這些值,那么在 DOMContentLoaded 上,瀏覽器會(huì)嘗試自動(dòng)填充它們(如果得到了用戶(hù)允許)。

因此,如果 DOMContentLoaded 被需要加載很長(zhǎng)時(shí)間的腳本延遲觸發(fā),那么自動(dòng)填充也會(huì)等待。你可能在某些網(wǎng)站上看到過(guò)(如果你使用瀏覽器自動(dòng)填充)—— 登錄名/密碼字段不會(huì)立即自動(dòng)填充,而是在頁(yè)面被完全加載前會(huì)延遲填充。這實(shí)際上是 DOMContentLoaded 事件之前的延遲。

window.onload

當(dāng)整個(gè)頁(yè)面,包括樣式、圖片和其他資源被加載完成時(shí),會(huì)觸發(fā) window 對(duì)象上的 load 事件??梢酝ㄟ^(guò) onload 屬性獲取此事件。

下面的這個(gè)示例正確顯示了圖片大小,因?yàn)?nbsp;window.onload 會(huì)等待所有圖片加載完畢:

<script>
  window.onload = function() { // 也可以用 window.addEventListener('load', (event) => {
    alert('Page loaded');

    // 此時(shí)圖片已經(jīng)加載完成
    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
  };
</script>

<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0" rel="external nofollow"  rel="external nofollow" >

window.onunload

當(dāng)訪(fǎng)問(wèn)者離開(kāi)頁(yè)面時(shí),window 對(duì)象上的 unload 事件就會(huì)被觸發(fā)。我們可以在那里做一些不涉及延遲的操作,例如關(guān)閉相關(guān)的彈出窗口。

有一個(gè)值得注意的特殊情況是發(fā)送分析數(shù)據(jù)。

假設(shè)我們收集有關(guān)頁(yè)面使用情況的數(shù)據(jù):鼠標(biāo)點(diǎn)擊,滾動(dòng),被查看的頁(yè)面區(qū)域等。

自然地,當(dāng)用戶(hù)要離開(kāi)的時(shí)候,我們希望通過(guò) unload 事件將數(shù)據(jù)保存到我們的服務(wù)器上。

有一個(gè)特殊的 navigator.sendBeacon(url, data) 方法可以滿(mǎn)足這種需求,詳見(jiàn)規(guī)范 https://w3c.github.io/beacon/

它在后臺(tái)發(fā)送數(shù)據(jù),轉(zhuǎn)換到另外一個(gè)頁(yè)面不會(huì)有延遲:瀏覽器離開(kāi)頁(yè)面,但仍然在執(zhí)行 sendBeacon。

使用方式如下:

let analyticsData = { /* 帶有收集的數(shù)據(jù)的對(duì)象 */ };

window.addEventListener("unload", function() {
  navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
});
  • 請(qǐng)求以 POST 方式發(fā)送。
  • 我們不僅能發(fā)送字符串,還能發(fā)送表單以及其他格式的數(shù)據(jù),在 Fetch 一章有詳細(xì)講解,但通常它是一個(gè)字符串化的對(duì)象。
  • 數(shù)據(jù)大小限制在 64kb。

當(dāng) sendBeacon 請(qǐng)求完成時(shí),瀏覽器可能已經(jīng)離開(kāi)了文檔,所以就無(wú)法獲取服務(wù)器響應(yīng)(對(duì)于分析數(shù)據(jù)來(lái)說(shuō)通常為空)。

還有一個(gè) keep-alive 標(biāo)志,該標(biāo)志用于在 fetch 方法中為通用的網(wǎng)絡(luò)請(qǐng)求執(zhí)行此類(lèi)“離開(kāi)頁(yè)面后”的請(qǐng)求。你可以在 Fetch API 一章中找到更多相關(guān)信息。

如果我們要取消跳轉(zhuǎn)到另一頁(yè)面的操作,在這里做不到。但是我們可以使用另一個(gè)事件 —— onbeforeunload。

window.onbeforeunload

如果訪(fǎng)問(wèn)者觸發(fā)了離開(kāi)頁(yè)面的導(dǎo)航(navigation)或試圖關(guān)閉窗口,beforeunload 處理程序?qū)⒁筮M(jìn)行更多確認(rèn)。

如果我們要取消事件,瀏覽器會(huì)詢(xún)問(wèn)用戶(hù)是否確定。

你可以通過(guò)運(yùn)行下面這段代碼,然后重新加載頁(yè)面來(lái)進(jìn)行嘗試:

window.onbeforeunload = function() {
  return false;
};

由于歷史原因,返回非空字符串也被視為取消事件。在以前,瀏覽器曾經(jīng)將其顯示為消息,但是根據(jù) 現(xiàn)代規(guī)范 所述,它們不應(yīng)該這樣。

這里有個(gè)例子:

window.onbeforeunload = function() {
  return "有未保存的值。確認(rèn)要離開(kāi)嗎?";
};

它的行為已經(jīng)改變了,因?yàn)橛行┱鹃L(zhǎng)通過(guò)顯示誤導(dǎo)性和惡意信息濫用了此事件處理程序。所以,目前一些舊的瀏覽器可能仍將其顯示為消息,但除此之外 —— 無(wú)法自定義顯示給用戶(hù)的消息。

?event.preventDefault()? 在 ?beforeunload? 處理程序中不起作用

這聽(tīng)起來(lái)可能很奇怪,但大多數(shù)瀏覽器都會(huì)忽略 event.preventDefault()

這意味著,以下代碼可能不起作用:

window.addEventListener("beforeunload", (event) => {
  // 不起作用,所以這個(gè)事件處理程序沒(méi)做任何事兒
  event.preventDefault();
});

相反,在這樣的處理程序中,應(yīng)該將 event.returnValue 設(shè)置為一個(gè)字符串,以獲得類(lèi)似于上面代碼的結(jié)果:

window.addEventListener("beforeunload", (event) => {
  // 起作用,與在 window.onbeforeunload 中 return 值的效果是一樣的
  event.returnValue = "有未保存的值。確認(rèn)要離開(kāi)嗎?";
});

readyState

如果我們?cè)谖臋n加載完成之后設(shè)置 DOMContentLoaded 事件處理程序,會(huì)發(fā)生什么?

很自然地,它永遠(yuǎn)不會(huì)運(yùn)行。

在某些情況下,我們不確定文檔是否已經(jīng)準(zhǔn)備就緒。我們希望我們的函數(shù)在 DOM 加載完成時(shí)執(zhí)行,無(wú)論現(xiàn)在還是以后。

document.readyState 屬性可以為我們提供當(dāng)前加載狀態(tài)的信息。

它有 3 個(gè)可能值:

  • ?loading? —— 文檔正在被加載。
  • ?interactive? —— 文檔被全部讀取。
  • ?complete? —— 文檔被全部讀取,并且所有資源(例如圖片等)都已加載完成。

所以,我們可以檢查 document.readyState 并設(shè)置一個(gè)處理程序,或在代碼準(zhǔn)備就緒時(shí)立即執(zhí)行它。

像這樣:

function work() { /*...*/ }

if (document.readyState == 'loading') {
  // 仍在加載,等待事件
  document.addEventListener('DOMContentLoaded', work);
} else {
  // DOM 已就緒!
  work();
}

還有一個(gè) readystatechange 事件,會(huì)在狀態(tài)發(fā)生改變時(shí)觸發(fā),因此我們可以打印所有這些狀態(tài),就像這樣:

// 當(dāng)前狀態(tài)
console.log(document.readyState);

// 狀態(tài)改變時(shí)打印它
document.addEventListener('readystatechange', () => console.log(document.readyState));

readystatechange 事件是跟蹤文檔加載狀態(tài)的另一種機(jī)制,它很早就存在了。現(xiàn)在則很少被使用。

但是為了完整起見(jiàn),讓我們看看完整的事件流。

這是一個(gè)帶有 <iframe>,<img> 和記錄事件的處理程序的文檔:

<script>
  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" rel="external nofollow"  id="img">
<script>
  img.onload = () => log('img onload');
</script>

此示例運(yùn)行 在 sandbox 中。

典型輸出:

  1. [1] initial readyState:loading
  2. [2] readyState:interactive
  3. [2] DOMContentLoaded
  4. [3] iframe onload
  5. [4] img onload
  6. [4] readyState:complete
  7. [4] window onload

方括號(hào)中的數(shù)字表示發(fā)生這種情況的大致時(shí)間。標(biāo)有相同數(shù)字的事件幾乎是同時(shí)發(fā)生的(± 幾毫秒)。

  • 在 ?DOMContentLoaded? 之前,?document.readyState? 會(huì)立即變成 ?interactive?。它們倆的意義實(shí)際上是相同的。
  • 當(dāng)所有資源(?iframe? 和 ?img?)都加載完成后,?document.readyState? 變成 ?complete?。這里我們可以發(fā)現(xiàn),它與 ?img.onload?(?img? 是最后一個(gè)資源)和 ?window.onload? 幾乎同時(shí)發(fā)生。轉(zhuǎn)換到 ?complete? 狀態(tài)的意義與 ?window.onload? 相同。區(qū)別在于 ?window.onload? 始終在所有其他 ?load? 處理程序之后運(yùn)行。

總結(jié)

頁(yè)面生命周期事件:

  • 當(dāng) DOM 準(zhǔn)備就緒時(shí),?document? 上的 ?DOMContentLoaded? 事件就會(huì)被觸發(fā)。在這個(gè)階段,我們可以將 JavaScript 應(yīng)用于元素。
    • 諸如 ?<script>...</script>? 或 ?<script src="..."></script>? 之類(lèi)的腳本會(huì)阻塞 ?DOMContentLoaded?,瀏覽器將等待它們執(zhí)行結(jié)束。
    • 圖片和其他資源仍然可以繼續(xù)被加載。
  • 當(dāng)頁(yè)面和所有資源都加載完成時(shí),?window? 上的 ?load? 事件就會(huì)被觸發(fā)。我們很少使用它,因?yàn)橥ǔo(wú)需等待那么長(zhǎng)時(shí)間。
  • 當(dāng)用戶(hù)想要離開(kāi)頁(yè)面時(shí),?window? 上的 ?beforeunload? 事件就會(huì)被觸發(fā)。如果我們?nèi)∠@個(gè)事件,瀏覽器就會(huì)詢(xún)問(wèn)我們是否真的要離開(kāi)(例如,我們有未保存的更改)。
  • 當(dāng)用戶(hù)最終離開(kāi)時(shí),?window? 上的 ?unload? 事件就會(huì)被觸發(fā)。在處理程序中,我們只能執(zhí)行不涉及延遲或詢(xún)問(wèn)用戶(hù)的簡(jiǎn)單操作。正是由于這個(gè)限制,它很少被使用。我們可以使用 ?navigator.sendBeacon? 來(lái)發(fā)送網(wǎng)絡(luò)請(qǐng)求。
  • ?document.readyState? 是文檔的當(dāng)前狀態(tài),可以在 ?readystatechange? 事件中跟蹤狀態(tài)更改:
    • ?loading? —— 文檔正在被加載。
    • ?interactive? —— 文檔已被解析完成,與 ?DOMContentLoaded? 幾乎同時(shí)發(fā)生,但是在 ?DOMContentLoaded? 之前發(fā)生。
    • ?complete? —— 文檔和資源均已加載完成,與 ?window.onload? 幾乎同時(shí)發(fā)生,但是在 ?window.onload? 之前發(fā)生。


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)