在 JavaScript 的傳統事件模型中,load 是頁面中最早被觸發的事件。不過當使用 load 事件來初始化頁面時可能會存在一個問題,就是當頁面中包含很大的檔案時,load 事件需要等到所有影象全部載入完成之後才會被觸發。也許使用者希望某些指令碼能夠在頁面結構載入完畢之後就能夠被執行。要怎麼辦呢?
這時可以考慮使用 DOMContentLoaded 事件型別。作為 DOM 標準事件,它是在 DOM 文件結構載入完畢的時候觸發的,因此要比 load 事件型別先被觸發。目前,Mozilla 和 Opera 新版本已經支援了該事件。而 IE 和 Safari 瀏覽器還不支援。
範例1
如果在標準 DOM 中,可以這樣設計。
<script>
window.onload = f1;
if (document.addEventListener) {
document.addEventListener ("DOMContentLoaded", f, false);
}
function f () { alert("我要提前執行了"); }
function f1 () { alert("頁面初始化完畢"); }
</script>
<img src="Winter.jpg">
這樣,在圖片載入之前會彈出“我要提前執行了”的提示資訊,而當圖片載入完畢之後會彈出“頁面初始化完畢”。這說明在頁面 HTML 結構載入完畢之後觸發 DOMContentLoaded 事件型別,也就是說,在文件標籤載入完畢時觸發該事件並呼叫函數 f(),然後,當文件所有內容載入完畢(包括圖片下載完畢)時才觸發 load 事件型別,並呼叫函數 f1()。
範例2
由於 IE 事件模型不支援 DOMContentLoaded 事件型別,為了實現相容處理,需要運用一點小技巧,即在文件中寫入一個新的 script 元素,但是該元素會延遲到檔案最後載入。然後,使用 Script 物件的 onreadystatechange 方法進行類似的 readyState 檢查後及時呼叫載入事件。
if (window.ActiveXObject) { //相容IE事件模型
document.write ("<script id=ie_onload defer src=javascript:void(0)></script"); //寫入指令碼標籤
document.getElementById("ie_load").onreadystatechange = function () {
//判斷指令碼標籤的狀態
if (this.readyState == "complete") { //如果狀態為完成,則說明文件結構載入已完畢
this.onreadystatechange = null; //清空當前反腐
f(); //呼叫預先執行的回撥函數
}
}
}
寫入的 <script> 標籤中包含了 defer屬性,defer 表示“延期”的意思,使用 defer 屬性可以讓指令碼在整個頁面裝載完成之後在解析,而非邊載入邊解析。這對於只包含事件觸發的指令碼來說,可以提高整個頁面的載入速度。與 src 屬性聯合使用,還可以使這些指令碼在後台被下載,而前台的內容正常顯示給使用者。目前只有 IE 事件模型支援該屬性。當定義了 defer 屬性後,<script> 標籤中就不應包含 document.write 命令,因為 document.write 將產生直接輸出的效果,而且不包含任何立即執行指令碼要使用的全域性變數或者函數。
<script> 標籤在文件結構載入完畢之後才載入,於是,只要判斷它的狀態就可以確定當前文件結構是否已經載入完畢並觸發響應的事件。
範例3
針對 Safari 瀏覽器,可以使用 setInterval() 函數週期性的檢查 document 物件的 readyState 屬性,隨時監控文件是否載入完畢,如果完成則呼叫回撥函數。
if (/WebKit/i.test(navigator.userAgent)) { //相容Safari瀏覽器
var _timer = setInterval (function () { //定義時間監測器
if (/loaded|complete/.test(document.readyState) { //如果當前狀態顯示完成
clearInterval(_timer); //清空時間監測器
f(); //呼叫預先執行的回撥函數
}
}, 10);
}
把上面 3 段條件合併在一起即可實現相容不同瀏覽器的 DomContentLoaded 事件處理常式。