# 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  如上圖所示, HTML 文件被解析時如果遇見(同步)指令碼,則停止解析,先去載入指令碼,然後執行,執行結束後繼續解析 HTML 文件。HTML文件解析完畢後觸發DOMContentLoaded。 ### async 《JavaScript高階程式設計》一書的解釋是:帶async的指令碼一定會在load事件之前執行,可能會在DOMContentLoaded之前或之後執行。 為什麼async指令碼可能會在DOMContentLoaded之前或之後執行呢?或者說,為什麼DOMContentLoaded事件的觸發既可能在async指令碼執行前、又可能在async指令碼執行後呢? 這是因為,async 標籤的指令碼載入完畢的時間有兩種情況: 情況1: HTML 還沒有被解析完的時候,async指令碼已經載入完了,那麼 HTML 停止解析,去執行指令碼,指令碼執行完畢後觸發DOMContentLoaded事件。如下圖所示:  情況2: HTML 解析完了之後,async指令碼才載入完,然後再執行指令碼,那麼在HTML解析完畢、async指令碼還沒載入完的時候就觸發DOMContentLoaded事件。如下圖所示:  總之, DomContentLoaded 事件只關注 HTML 是否被解析完,而不關注 async 指令碼。 ### defer 如果 script 標籤中包含 defer,那麼這一塊指令碼將不會影響 HTML 文件的解析,而是等到 HTML 解析完成後才會執行。而 DOMContentLoaded 只有在 defer 指令碼執行結束後才會被觸發。 defer指令碼同樣包含兩種情況: 情況1:HTML還沒解析完成時,defer指令碼已經載入完畢,那麼defer指令碼將等待HTML解析完成後再執行。defer指令碼執行完畢後觸發DOMContentLoaded事件。如下圖所示  情況2:HTML解析完成時,defer指令碼還沒載入完畢,那麼defer指令碼繼續載入,載入完成後直接執行,執行完畢後觸發DOMContentLoaded事件。如下圖所示:  對於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)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up