---
# System prepended metadata

title: Why would you use something like the load event? ...

---

# Why would you use something like the load event? ...

> Why would you use something like the load event?
> Does this event have disadvantages? Do you know any alternatives, and why would you use those?
> [color=#f24]
:::info
:bookmark: 在頁面載入完成後需要立即執行某些動作時使用 load event(例如顯示列印頁面，或是將焦點對準 input 欄位等等)。

:bookmark: load event 的缺點在於它是在網頁載入完畢後才觸發，也就是要等待script、stylesheet、圖片、連結等資源加載完畢後才會執行。

:bookmark: 可以使用 DOMContentLoaded event 來替代 load event，他會在 DOM 被建構時就觸發。
也可以使用 將 defer 加在 script Tag 的方式(但要確保腳本是外部加載)。好處是瀏覽器會立即開始載入script，在DOM全部都解析完畢之後執行。
:::

[複習Load 事件 vs DOMContentLoaded 事件](https://hackmd.io/5XjisNKFSdaiCEZGZXZdEQ)

## 例子：頁面載入後焦點在 input 欄位

``` JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <input id="ip" type="text" placeholder="input something..."　/>
  <script>
    window.onload = function(){
      document.getElementById("ip").focus()
    }
  </script>
</body>
</html>
```

## load event 的缺點，在整個網頁載入完畢後才觸發

正常情況下我們都會將 script標籤 寫在 body標籤 的結尾標籤之前，得以讓程式碼能夠解析 DOM 之後才執行。為了方便解釋替代方法，接下來都以一樣把 script標籤 寫在其他地方的前提為例子。(寫在 head標籤 裡面或是緊接在 body 的開頭標籤之後)


``` JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <script>
    window.onload = function(){
      document.getElementById("ip").focus()
      alert('Images Loaded.')
    }
  </script>
  <input id="ip" type="text" placeholder="input something..."　/>
  <hr>
  <img src="https://images.unsplash.com/photo-1473603477862-9d352d4615e1?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=967&q=80"/>
  <br>
  <img src="https://images.unsplash.com/photo-1590239215418-8c78f7e21af4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1351&q=80" />
</body>
</html>
```

### 解決方法：用 DOMContentLoaded 替代 load event
``` JavaScript
 <script>
    document.addEventListener('DOMContentLoaded', function(){
      document.getElementById("ip").focus()
      alert('Images Loaded.')
    })
 </script>
```
在DOM 樹構建完成就會被觸發，不需等待圖片讀取完成
### 解決方法：將 async 或 defer 添加在 script 標籤

我們其實把 script標籤 寫在 body 的結尾標籤前就解決問題了，但是這種解決方案遠非完美。
例如，瀏覽器只有在下載了完整的 HTML 文檔之後才會注意到該script。對於長的 HTML 文檔來說，這樣可能會造成明顯的延遲。

這對於網速快的地方來說不值一提，他們不會感受到這種延遲。但是這個世界上仍然有很多地區的人們所使用的網絡速度很慢。
使用aysnc 或 defer 達成非同步載入，使得HTML被瀏覽器解析的同時，JS檔案也被加載。
**test.html**
```htmlmixed=JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <script src="test.js" defer></script>
  <input id="ip" type="text" placeholder="input something..."　/>
  <hr>
  <img src="https://images.unsplash.com/photo-1473603477862-9d352d4615e1?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=967&q=80"/>
  <br>
  <img src="https://images.unsplash.com/photo-1590239215418-8c78f7e21af4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1351&q=80" />
</body>
</html>
```
**test.js**
``` JavaScript
document.getElementById("ip").focus()
alert('Images Loaded.')
```

:::danger
:bulb:若不添加 defer 達成非同步載入，則會報錯：
Uncaught TypeError: Cannot read property 'focus' of null
:::
## 題外補充: async、defer與DOMContentLoaded的關係
### 一般同步的情況：sync
![](https://i.imgur.com/953S33d.png)

如上圖所示， HTML 文件被解析時如果遇見（同步）指令碼，則停止解析，先去載入指令碼，然後執行，執行結束後繼續解析 HTML 文件。HTML文件解析完畢後觸發DOMContentLoaded。

### async
《JavaScript高階程式設計》一書的解釋是：帶async的指令碼一定會在load事件之前執行，可能會在DOMContentLoaded之前或之後執行。

為什麼async指令碼可能會在DOMContentLoaded之前或之後執行呢？或者說，為什麼DOMContentLoaded事件的觸發既可能在async指令碼執行前、又可能在async指令碼執行後呢？ 這是因為，async 標籤的指令碼載入完畢的時間有兩種情況：

情況1： HTML 還沒有被解析完的時候，async指令碼已經載入完了，那麼 HTML 停止解析，去執行指令碼，指令碼執行完畢後觸發DOMContentLoaded事件。如下圖所示：
![](https://i.imgur.com/jGAHsrz.png)

情況2： HTML 解析完了之後，async指令碼才載入完，然後再執行指令碼，那麼在HTML解析完畢、async指令碼還沒載入完的時候就觸發DOMContentLoaded事件。如下圖所示：
![](https://i.imgur.com/v1dTURr.png)


總之， DomContentLoaded 事件只關注 HTML 是否被解析完，而不關注 async 指令碼。

### defer
如果 script 標籤中包含 defer，那麼這一塊指令碼將不會影響 HTML 文件的解析，而是等到 HTML 解析完成後才會執行。而 DOMContentLoaded 只有在 defer 指令碼執行結束後才會被觸發。

defer指令碼同樣包含兩種情況：

情況1：HTML還沒解析完成時，defer指令碼已經載入完畢，那麼defer指令碼將等待HTML解析完成後再執行。defer指令碼執行完畢後觸發DOMContentLoaded事件。如下圖所示
![](https://i.imgur.com/7nvEXlI.png)

情況2：HTML解析完成時，defer指令碼還沒載入完畢，那麼defer指令碼繼續載入，載入完成後直接執行，執行完畢後觸發DOMContentLoaded事件。如下圖所示：
![](https://i.imgur.com/cZZhcfn.png)


對於defer指令碼，《JavaScript高階程式設計》一書的說法是:“按照HTML5規範，兩個defer指令碼會安裝它們出現的先後順序執行，兩個指令碼會在DOMContentLoaded之前執行。”這和我們上面的分析一致。
然而，該書接下來說，“但事實上，defer指令碼不一定會按順序執行，也不一定會在DOMContentLoaded之前執行。”這是一個待再繼續研究測試的問題

---
參考資料
[When to use window.onload?](https://stackoverflow.com/questions/20180251/when-to-use-window-onload)
[async、defer与DOMContentLoaded的执行先后关系](https://blog.csdn.net/zyj0209/article/details/79698430)
[Scripts: async, defer](https://javascript.info/script-async-defer)
