當前位置:
首頁 > 知識 > DOM探索之-domReady

DOM探索之-domReady

還記得剛開始學習JavaScript時候,經常會犯這樣的錯誤:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dom not ready</title>
<script>
document.getElementById("header").style.color = "red";
</script>
</head>
<body>
<h1 id="header">這裡是h1元素包含的內容</h1>
</body>
</html>

最後發現結果並不是我們想要的,文字並沒有變成紅色,我想最先入門學習JavaScript操作DOM時候多多少少會遇到這種困惑和錯誤,其實出現這種問題的原因就是我們沒有區分HTML標籤和DOM節點的區別的緣故了,由這個問題就引出下面要說的domReady和瀏覽器渲染解析原理了。


什麼是domRead

html是一種標記語言,它告訴我們這個頁面有什麼內容,但行為交互是需要通過DOM操作來實現的。我們不要以為有兩個尖括弧就以為它是一個DOM了,html標籤要通過瀏覽器解析才會變成DOM節點,當我們向地址欄傳入一個url的時候,我們開始載入頁面,就能看到內容,在這期間就有一個DOM節點構建的過程。節點是以樹的形式組織的,當頁面上所有的html都轉換為節點以後,就叫做DOM樹構建完畢,簡稱為domReady。

那麼瀏覽器是如何將html標籤解析變成DOM節點的呢?

實際上瀏覽器是通過渲染引擎來實現的。渲染引擎的職責就是把請求的內容顯示到瀏覽器屏幕上。默認情況下渲染引擎可以顯示html、xml文檔及圖片。通過插件(瀏覽器擴展)它可以顯示其他類型的文檔,比如我們安裝pdf viewer插件,我們就可以顯示pdf文檔。這裡專註渲染引擎的主要用途,即是將css格式化的html和圖片在瀏覽器上進行顯示。

瀏覽器渲染引擎的基本渲染流程

瀏覽器渲染要做的事就是把CSS,HTML,圖片等靜態資源展示到用戶眼前。

DOM探索之-domReady


渲染引擎首先通過網路獲得所請求文檔的內容,通常以8k分塊的方法來完成:

DOM探索之-domReady

上圖就是html渲染的基本過程,但這並不包含解析過程中瀏覽器載入外部資源,比如圖片、腳本、iframe等的一些過程。說白了,上面的4步僅僅是html結構的渲染過程。而外部資源的載入在html結構的渲染過程中是貫徹始終的,即便繪製DOM節點已經完成,而外部資源仍然可能正在載入或者尚未載入。


Webkit主要渲染流程

Firefox瀏覽器Gecko渲染流程跟Webkit內核渲染類似,大同小異,WebKit 和 Gecko 使用的術語略有不同,但整體流程是基本相同的。這裡以Webkit內核作為例子來說明瀏覽器渲染的主要流程。

瀏覽器的渲染原理並非三言兩語,幾個圖就能說明白的,上圖說的只是介紹一個大環節的過程和步驟,這裡拋磚引玉象徵性說個大概,更多關於瀏覽器內部工作原理的文章,請閱讀:瀏覽器的工作原理:新式網路瀏覽器幕後揭秘


domReady的實現策略

上面的各個代碼實例中,並沒有考慮domReady,程序也能正常運行,因為我們把javascript代碼寫在了body元素最後的位置。因為瀏覽器是從上到下,從左向右渲染元素的,這樣實例中的js代碼一定在domReady之後去執行的。那為什麼還要用domReady呢?事實上,我們在編寫大型項目的時候,js文件往往非常多,而且之間會相互調用,大多數都是外部引用的,不把js代碼直接寫在頁面上。這樣的話,如果有個domReady這個方法,我們想用它就調用,不管邏輯代碼寫在哪裡,都是等到domReady之後去執行的。

window.onload方法,表示當頁面所有的元素都載入完畢,並且所有要請求的資源也載入完畢才觸發執行function這個匿名函數裡邊的具體內容。這樣肯定保證了代碼在domReady之後執行。使用window.onload方法在文檔外部資源不多的情況下不會有什麼問題,但是當頁面中有大量遠程圖片或要請求的遠程資源時,我們需要讓js在點擊每張圖片時,進行相應的操作,如果此時外部資源還沒有載入完畢,點擊圖片是不會有任何反應的,大大降低了用戶體驗。那既然window.onload方法不可行,又該怎麼做呢

你肯定想到了jquery中的$(document).ready(function(){})方法了,其實jquery中的domReady應該和window.onload的實現原理是大同小異的。為了解決window.onload的短板,w3c 新增了一個 DOMContentLoaded 事件。

這裡提到了DOMContentLoaded事件,這裡由於篇幅有限,就不多做介紹,這裡面也有很多細節可以學習,有興趣的童鞋,可以看看我之前收藏的兩篇文章:

你不知道的 DOMContentLoaded

淺談DOMContentLoaded事件及其封裝方法

學習就是一個無底洞,因為深不可測,才讓人不斷探索。

參考jquery中domReady的實現原理,來看一下javascript中domReady的實現策略。

在頁面的DOM樹創建完成後(也就是HTML解析第一步完成)即觸發,而無需等待其他資源的載入。即domReady實現策略:

1. 支持DOMContentLoaded事件的,就使用DOMContentLoaded事件。
2. 不支持的就用來自Diego Perini發現的著名Hack兼容。兼容原理大概就是通過IE中的document,
documentElement.doScroll("left")來判斷DOM樹是否創建完畢。

JavaScript實現domReady,【domReady.js】

function myReady(fn){
//對於現代瀏覽器,對DOMContentLoaded事件的處理採用標準的事件綁定方式
if ( document.addEventListener ) {
document.addEventListener("DOMContentLoaded", fn, false);
} else {
IEContentLoaded(fn);
}
//IE模擬DOMContentLoaded
function IEContentLoaded (fn) {
var d = window.document;
var done = false;

//只執行一次用戶的回調函數init()
var init = function () {
if (!done) {
done = true;
fn();
}
};
(function () {
try {
// DOM樹未創建完之前調用doScroll會拋出錯誤
d.documentElement.doScroll("left");
} catch (e) {
//延遲再試一次~
setTimeout(arguments.callee, 50);
return;
}
// 沒有錯誤就表示DOM樹創建完畢,然後立馬執行用戶回調
init();
})();
//監聽document的載入狀態
d.onreadystatechange = function() {
// 如果用戶是在domReady之後綁定的函數,就立馬執行
if (d.readyState == "complete") {
d.onreadystatechange = null;
init();
}
}
}
}

在頁面中引入donReady.js文件,引用myReady(回調函數)方法即可。

感興趣的童鞋可以看看各個主流框架domReady的實現:點擊我查看


一個小栗子看二者差異性

下面通過一個案例,來比較domReady與window.onload實現的不同,很明顯,onload事件是要在所有請求都完成之後才執行,而domReady利用hack技術,在載入完dom樹之後就能執行,所以domReady比onload執行時間更早,建議採用domReady。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>domReady與window.onload</title>
<script src="domReady.js"></script>
</head>
<body>
<div id="showMsg"></div>
<div>
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zofelhdj20xc0xc42s.jpg" alt="">
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zofahw3j20m80etq4a.jpg" alt="">
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zoi3ny6j20l20dw4gd.jpg" alt="">
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zog3tauj20m80et0uw.jpg" alt="">
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zofi2o5j20m80ettaq.jpg" alt="">
<img src="http://ww1.sinaimg.cn/large/ae49ba57gy1fe9zohjuvhj20tb0cdwvp.jpg" alt="">
</div>
<script>
var d = document;
var msgBox = d.getElementById("showMsg");
var imgs = d.getElementsByTagName("img");
var time1 = null,
time2 = null;
myReady(function() {
msgBox.innerHTML += "dom已載入!<br>";
time1 = new Date().getTime();
msgBox.innerHTML += "時間戳:" + time1 + "<br>";
});
window.onload = function() {
msgBox.innerHTML += "onload已載入!<br>";
time2 = new Date().getTime();
msgBox.innerHTML += "時間戳:" + time2 + "<br>";
msgBox.innerHTML += "domReady比onload快:" + (time2 - time1) + "ms<br>";
};
</script>
</body>
</html>

執行結果對比,發現DomReady比onload快樂2秒多。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 極客教程 的精彩文章:

前端為什麼使用框架?解決了哪些問題?
教你百家號一篇文章就能收益幾百,月入過萬簡簡單單

TAG:極客教程 |