頁面生命週期:DOMContentLoaded, load, beforeunload, unload

頁面生命週期:DOMContentLoaded, load, beforeunload, unload

原文地址:http://javascript.info/onload...javascript

HTML頁面的生命週期有如下三個重要事件:css

  • DOMContentLoaded — 瀏覽器已經徹底加載了HTML,DOM樹已經構建完畢,可是像是 <img> 和樣式表等外部資源可能並無下載完畢。
  • load — 瀏覽器已經加載了全部的資源(圖像,樣式表等)。
  • beforeunload/unload -- 當用戶離開頁面的時候觸發。

每一個事件都有特定的用途html

  • DOMContentLoaded -- DOM加載完畢,因此js能夠訪問全部DOM節點,初始化界面。
  • load -- 附加資源已經加載完畢,能夠在此事件觸發時得到圖像的大小(若是沒有被在HTML/CSS中指定)
  • beforeunload/unload -- 用戶正在離開頁面:能夠詢問用戶是否保存了更改以及是否肯定要離開頁面。

來看一下每一個事件的細節。java

DOMContentLoaded

DOMContentLoadeddocument 對象觸發。瀏覽器

咱們使用 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樹構建完畢以後就運行該事件,不過其實存在一些陷阱。框架

DOMContentLoaded 和腳本

當瀏覽器在解析HTML頁面時遇到了 <script>...</script> 標籤,將沒法繼續構建DOM樹(譯註:UI渲染線程與JS引擎是互斥的,當JS引擎執行時UI線程會被掛起),必須當即執行腳本。因此 DOMContentLoaded 有可能在全部腳本執行完畢後觸發。less

外部腳本(帶src的)的加載和解析也會暫停DOM樹構建,因此 DOMContentLoaded 也會等待外部腳本。

不過有兩個例外是帶asyncdefer的外部腳本,他們告訴瀏覽器繼續解析而不須要等待腳本的執行,因此用戶能夠在腳本加載完成前能夠看到頁面,有較好的用戶體驗。

asyncdefer屬性僅僅對外部腳本起做用,而且他們在src不存在時會被自動忽略。

它們都告訴瀏覽器繼續處理頁面上的內容,而在後臺加載腳本,而後在腳本加載完畢後再執行。因此腳本不會阻塞DOM樹的構建和頁面的渲染。

(譯註:其實這裏是不對的,帶有asyncdefer的腳本的下載是和HTML的下載與解析是異步的,可是js的執行必定是和UI線程是互斥的,像下面這張圖所示,async在下載完畢後的執行會阻塞HTML的解析)

他們有兩處不一樣:

async defer
順序 帶有async的腳本是優先執行先加載完的腳本,他們在頁面中的順序並不影響他們執行的順序。 帶有defer的腳本按照他們在頁面中出現的順序依次執行。
DOMContentLoaded 帶有async的腳本也許會在頁面沒有徹底下載完以前就加載,這種狀況會在腳本很小或本緩存,而且頁面很大的狀況下發生。 帶有defer的腳本會在頁面加載和解析完畢後執行,恰好在 DOMContentLoaded以前執行。

因此async用在那些徹底不依賴其餘腳本的腳本上。

### DOMContentLoaded and styles

External style sheets don't affect DOM, and so `DOMContentLoaded` does not wait for them.
外部樣式表並不會影響DOM,因此`DOMContentLoaded`並不會被他們阻塞。
But there's a pitfall: if we have a script after the style, then that script must wait for the stylesheet to execute:
不過仍然有一個陷阱:若是在樣式後面有一個內聯腳本,那麼腳本必須等待樣式先加載完。

<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事件。

使用帶asyncdefer的腳本的一個好處就是,他們不會阻塞DOMContentLoaded和瀏覽器自動補全。(譯註:其實執行仍是會阻塞的)

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">

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. [1] initial readyState:loading
  2. [2] readyState:interactive
  3. [2] DOMContentLoaded
  4. [3] iframe onload
  5. [4] readyState:complete
  6. [4] img onload
  7. [4] window onload

方括號中的數字表示他們發生的時間,真實的發生時間會更晚一點,不過相同數字的時間能夠認爲是在同一時刻被按順序觸發(偏差在幾毫秒以內)

  • document.readyStateDOMContentLoaded前一刻變爲interactive,這兩個事件能夠認爲是同時發生。
  • document.readyState 在全部資源加載完畢後(包括iframeimg)變成complete,咱們能夠看到completeimg.onloadwindow.onload幾乎同時發生,區別就是window.onload在全部其餘的load事件以後執行。

總結

頁面事件的生命週期:

  • DOMContentLoaded事件在DOM樹構建完畢後被觸發,咱們能夠在這個階段使用js去訪問元素。

    • asyncdefer的腳本可能尚未執行。
    • 圖片及其餘資源文件可能還在下載中。
  • load事件在頁面全部資源被加載完畢後觸發,一般咱們不會用到這個事件,由於咱們不須要等那麼久。
  • beforeunload在用戶即將離開頁面時觸發,它返回一個字符串,瀏覽器會向用戶展現並詢問這個字符串以肯定是否離開。
  • unload在用戶已經離開時觸發,咱們在這個階段僅能夠作一些沒有延遲的操做,因爲種種限制,不多被使用。
  • document.readyState表徵頁面的加載狀態,能夠在readystatechange中追蹤頁面的變化狀態:

    • loading — 頁面正在加載中。
    • interactive -- 頁面解析完畢,時間上和 DOMContentLoaded同時發生,不過順序在它以前。
    • complete -- 頁面上的資源都已加載完畢,時間上和window.onload同時發生,不過順序在他以前。
相關文章
相關標籤/搜索