讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議127:妥善使用DOMContentLoaded事件 >

建議127:妥善使用DOMContentLoaded事件

在傳統事件模型中,load是頁面中最早被觸發的事件。不過當使用load事件來初始化頁面時可能會存在一個問題,那就是當頁面中包含很大的文件時,load事件需要等到所有圖像全部載入完成之後才會被觸發。這時可以考慮使用DOMContentLoaded事件,作為DOM標準事件,它是在DOM文檔結構加載完畢的時候被觸發的,要比load事件先被觸發。目前,Firefox和Opera新版本已經支持了DOMContentLoaded事件,而IE和Safari瀏覽器還不支持。

在標準DOM中可以這樣設計:


<html>

<head>

<script language="javascript"type="text/javascript">

window.onload=f1;

if(document.addEventListener){

document.addEventListener("DOMContentLoaded",f,false);

}

function f{

alert("我提前執行了");

}

function f1{

alert("頁面初始化完畢");

}

</script>

</head>

<body>

<img src="Winter.jpg">

</body>

</html>


這樣,在圖片加載之前,會彈出「我提前執行了」的提示信息,而在圖片加載完畢後才會彈出「頁面初始化完畢」提示信息。這說明在頁面HTML結構加載完畢之後觸發DOMContentLoaded事件,也就是說,在文檔標籤加載完畢觸發該事件,並調用函數f,然後在文檔所有內容加載完畢(包括圖片下載完畢)才觸發load事件,並調用函數f1。

由於IE不支持DOMContentLoaded事件,為了實現兼容處理,我們需要運用一點小技巧,即在文檔中寫入一個新的script元素,不過該元素會延遲到文件最後加載。在使用script元素的onreadystatechange方法進行類似的readyState檢查後及時調用載入事件,代碼如下:


if(window.ActiveXObject){

document.write("<script id=ie_onload defer src=javascript:void(0)><\/script>");

document.getElementById("ie_onload").onreadystatechange=function{

if(this.readyState=="complete"){

this.onreadystatechange=null;

f;

}

}

}


在寫入的<script>標籤中包含了defer屬性,defer表示「延期」的意思,使用defer屬性可以讓腳本在整個頁面加載完成之後再解析,而非邊加載邊解析。這對於只包含事件觸發的腳本來說,可以提高整個頁面的加載速度。與src屬性聯合使用,還可以使這些腳本在後台被下載,前台的內容則正常顯示給用戶。目前只有IE支持defer屬性。在定義了defer屬性後,<script>標籤中就不應該包含document.write命令了,因為document.write將產生直接輸出效果,並且不包括任何立即執行腳本要使用的全局變量或函數。

由於<script>標籤在文檔結構加載完畢之後才加載,因此只要判斷它的狀態就可以確定當前文檔結構是否已經加載完畢,並觸發響應的事件。

針對Safari瀏覽器,我們可以使用setInterval函數週期性地檢查document對象的readyState屬性,隨時監控文檔是否加載完畢,如果加載完成則調用回調函數,代碼如下:


if(/WebKit/i.test(navigator.userAgent)){

var_timer=setInterval(function{

if(/loaded|complete/.test(document.readyState)){

clearInterval(_timer);

f;

}

},10);

}


把上面3段條件結構合併在一起即可實現兼容不同瀏覽器的DOMContentLoaded事件處理函數。