咱們常常看人們用:javascript
document.getElementById('xxx').style/left = "80px"
結果卻報錯說找不到元素,可是頁面上明明包含id爲xxx的這個元素,這實際上就是沒分清HTML標籤於DOM節點。HTML是一種標記語言,它告訴咱們頁面上有什麼內容。但行爲交互是須要經過DOM操做實現的,不要覺得那兩個尖括號的內容就是一個DOM。HTML標籤要通過瀏覽器解析纔會變成DOM節點。當咱們向地址欄傳入一個URL,開始加載頁面,到咱們看到內容,這期間就有一個DOM節點構建的過程。節點們是以樹的形式組織的,當頁面上全部HTML都轉爲節點,這就叫作DOM樹構建完成,簡稱之DomReady
。html
HTML轉爲爲DOM是一個複雜的過程,能夠參考這個:HTML TO DOM.java
咱們簡單說一下,瀏覽器是從上到下,從左到右,一個個字符串讀入,大體能夠認爲兩個同名的開標籤與閉標籤就是一個DOM(有的是沒有閉籤),這時就忽略掉它的兩個標籤間的內容。頁面上有許多標籤,但標籤會生成一樣多的DOM,由於有的標籤下只容許存在特定的子標籤,好比tr
下面必定是td
,th
, select
下面必定是opgroup
,option
,而option
下面,就算你寫了<span></span>
,它都會忽略掉,option
下面只存在文本,這就是咱們須要自定義下拉框的緣故。咱們說過,這順序是從上到下,有的元素很簡單,會構建得很快。但標籤存在src,href屬性,它會引用外部資源,這就要區別對待了。好比說,script標籤,它必定會等src指定的腳本文件加載下來,而後所有執行了裏面的腳本,纔會分析下一個標籤。這種現象叫作堵塞。堵塞是一種很是致命的現象,由於瀏覽器渲染引擎是單線程的,若是頭部腳本過多過大會致使白屏,影響用戶體驗,所以雅虎的20軍規就有一條提到,將全部script標籤放到body以後。此外,style標籤與link標籤,它們在加載樣式文件時是不會堵塞,但它們一旦異步加載好,就當即開始渲染已經構建好的元素節點們,這可能會引發重繪,這也影響速度。另外一個影響DOM樹構建的所以是iframe,它也會加載資源,雖然不會堵塞DOM構建,但它因爲是發出HTTP請求,而HTTP請求是有限,它會與父標籤的其餘須要加載外部資源的標籤產生競爭。咱們常常看到一些新聞網,上面會掛許多iframe廣告,這些頁面一開始加載時就很卡,也是這緣故。此外還有object元素,用來加載flash等等,這些東西都會影響到DOM樹的構建過程。所以在這時候,當咱們貿貿然,使用getElementById
,getElementsByTagName
獲取元素,而後操做它們,就會有很大機率碰到元素爲null的異常。這時,目標元素還能夠沒有轉換爲DOM節點,還只是一個普通的字符串呢!web
咱們又不能隨意寫一個瀏覽器
setTimeout(function(){ document.getElementById("xxx").style.left = "80px" }, 3000)
這徹底是靠蒙,可能有效,也可能失敗。所以得到全部標籤都轉換爲DOM節點的時機就很是重要。很早期,瀏覽器提供了一個window.onload
方法,但這東西是等到全部標籤變成DOM,而且外部資源、圖片、背景音樂什麼都加載好才觸發,時間上有點晚。幸虧,瀏覽器提供了一個document.readyState
屬性,當它變成complete時,說明這時機到了。但這是一個屬性,不是一個事件,須要使用不太精確的setInterval輪詢。如今,W3C終於紳士地提供了一個DOMContentLoaded
事件。安全
DOMContentLoaded
應該是最好用的,它是一個事件,表明着雖然stylesheet,images這些資源沒有加載好,但HTMLDocument徹底被加載並解析好了,已經能夠安全地操做DOM了。ruby
document.addEventListener("DOMContentLoaded", function(event) { console.log("DOM fully loaded and parsed"); });
load觸發的時機是全部的資源所有加載完成,這個時候才操做DOM實際上有點晚了。異步
document.addEventListener("load", function(event) { console.log("All resources finished loading!"); });
// alternative to DOMContentLoaded document.onreadystatechange = function () { if (document.readyState == "interactive") { console.log('DOMContentLoaded') } } //alternative to window.onload document.onreadystatechange = function(){ if(document.readyState == "completed"){ console.log('load') } }
修改自司徒正美的博文,原連接spa