1、DOM
DOM(Document Object Model)即文檔對象模型,是從文檔中抽象出來的,DOM 操作的對象就是文檔,DOM 將 HTML 文檔呈現(xiàn)為帶有元素、屬性和文本的樹結(jié)構(gòu),即節(jié)點(diǎn)樹。通過 DOM,JS 可創(chuàng)建動態(tài)的 HTML,可以使網(wǎng)頁顯示動態(tài)效果并實現(xiàn)與用戶的交互功能。DOM 給我們提供了用程序來動態(tài)控制 HTML 的接口(也叫API),因此 DOM 處在 JS 賦予 HTML 具備動態(tài)交互和效果能力的核心地位上。想要安全的操作 DOM,必須等到頁面中所有的 HTML 都解析成 DOM 節(jié)點(diǎn),才能進(jìn)行操作,因此我們必須了解 DOMReady。在這之前我們先來回顧一下 DOM節(jié)點(diǎn)。
(1)、常見的節(jié)點(diǎn)類型
常見的節(jié)點(diǎn)類型有以下 7 種:
節(jié)點(diǎn)類型 | 說明 | 數(shù)值常量 |
Element(元素節(jié)點(diǎn)) | HTML標(biāo)簽元素。 | 1 |
Attr(屬性節(jié)點(diǎn)) | 元素節(jié)點(diǎn)的屬性。 | 2 |
Text(文本節(jié)點(diǎn)) | 元素節(jié)點(diǎn)或?qū)傩怨?jié)點(diǎn)中的文本內(nèi)容。 | 3 |
Comment(注釋節(jié)點(diǎn)) | 表示注釋的內(nèi)容。 | 8 |
Document(文檔節(jié)點(diǎn)) | 表示整個文檔(DOM 樹的根節(jié)點(diǎn),即 document )。 | 9 |
DocumentType(文檔類型節(jié)點(diǎn)) | <!DOCTYPE html>就是文檔類型節(jié)點(diǎn)。 | 10 |
DocumentFragment(文檔片段節(jié)點(diǎn)) | 表示文檔的一部分或者是一段,它不屬于文檔樹。 | 11 |
(2)、節(jié)點(diǎn)類型說明
元素節(jié)點(diǎn),就是 HTML 標(biāo)簽元素,如 <div> 、 <p>、<ul> 等。
屬性節(jié)點(diǎn),就是元素節(jié)點(diǎn)的屬性,如 id 、class 、name 等。屬性節(jié)點(diǎn)不能被看作是元素節(jié)點(diǎn),因而在 DOM 中屬性沒有被認(rèn)為是文檔樹的一部分,換句話就是說屬性節(jié)點(diǎn)是包含他的元素節(jié)點(diǎn)的一部分,他并不作為一個單獨(dú)的節(jié)點(diǎn)在文檔樹中出現(xiàn)。
文本節(jié)點(diǎn),就是只包含文本內(nèi)容的節(jié)點(diǎn)。可以包含更多信息,也可以只包含空白,在文檔樹中元素的文本內(nèi)容和屬性的文本內(nèi)容都是由文本節(jié)點(diǎn)來表示的。
注釋節(jié)點(diǎn),就是文檔中的注釋,其形式為 <!-- 這是一個注釋 -->。
文檔節(jié)點(diǎn),就是整個文檔,是文檔樹的根節(jié)點(diǎn),是文檔中所有其他節(jié)點(diǎn)的父節(jié)點(diǎn)。這里需要注意:文檔節(jié)點(diǎn)并不是 HTML 文檔的根元素,在構(gòu)造 DOM 樹時,根元素并不適合作為根節(jié)點(diǎn),于是就有了文檔節(jié)點(diǎn),而根元素是作為文檔節(jié)點(diǎn)的子節(jié)點(diǎn)出現(xiàn)的。將整個 HTML 文檔代碼之上看為一個文檔節(jié)點(diǎn),這個節(jié)點(diǎn)下包含一個文檔類型節(jié)點(diǎn) <!DOCTYPE html> 和一個元素節(jié)點(diǎn) <html>,兩個子節(jié)點(diǎn)。
文檔類型節(jié)點(diǎn),每一個 Document 都有一個 DocumentType 屬性,<!DOCTYPE html> 就是文檔類型節(jié)點(diǎn)。
文檔片段節(jié)點(diǎn),是輕量級的或最小的 Document 對象,它表示文檔的一部分或者是一段,它不屬于文檔樹。不過它有一種特殊的行為,這一行為非常有用,比如當(dāng)請求把一個DocumentFragment 節(jié)點(diǎn)插入到文檔的時候,插入的不是 DocumentFragment 自身,而是它所有的子孫節(jié)點(diǎn)。這時 DocumentFragment 就成了有用的占位符,暫時存放那些依次插入文檔的節(jié)點(diǎn),同時它還有利于實現(xiàn)文檔的剪切、復(fù)制、粘貼等操作。像 JS 代碼中插入元素所定義的變量,這個變量只是作為一個臨時的占位符。這就是所謂的文檔片段節(jié)點(diǎn)。
文檔片段也叫文檔碎片,創(chuàng)建一個文檔碎片可使用 document.createDocumentFragment() 方法,可直接給父節(jié)點(diǎn)下插入 n 個子節(jié)點(diǎn)。在理論上文檔碎片可以提高 DOM 操作性能。文檔碎片在低版本瀏覽器可以大大提高頁面性能,但是在高級的瀏覽器,幾乎性能沒有提高,而且還會影響性能。所以不建議使用,這東西基本是已經(jīng)淘汰了。下面是創(chuàng)建文檔碎片的實例。
實例:在 ul 元素下插入一個文檔碎片
<body> <ul id="listNode"></ul> <script> //創(chuàng)建一個文檔碎片 var frag = document.createDocumentFragment(); //使用循環(huán)設(shè)置創(chuàng)建10個li元素 for(var i = 0; i < 10; i++){ //創(chuàng)建一個li元素 var oLi = document.createElement('li'); //li元素顯示的內(nèi)容 oLi.innerHTML = 'list ' + i; //創(chuàng)建完畢后插入文檔碎片中 frag.appendChild(oLi); } //最后將文檔碎片插入到父節(jié)點(diǎn)ul元素下 document.getElementById('listNode').appendChild(frag); </script> </body>
(3)、節(jié)點(diǎn)類型、節(jié)點(diǎn)名稱和節(jié)點(diǎn)值
屬性的一系列操作是與元素的類型息息相關(guān)的,如果我們不對元素的節(jié)點(diǎn)類型作判斷,就不知道如何操作:例如:obj.xx = yy 還是 obj.setAttribute(xx, yy),setAttribute() 方法可添加一個新屬性并指定值,或者把一個現(xiàn)有的屬性設(shè)定為指定的值,如果我們知道節(jié)點(diǎn)的類型,就可以直接設(shè)置或者是使用方法設(shè)置,所以我們有必要判斷節(jié)點(diǎn)的類型,避免耗費(fèi)資源,造成意想不到的結(jié)果。判斷節(jié)點(diǎn)類型可使用 nodeType 屬性用數(shù)值常量進(jìn)行判斷,其操作很簡單,就是判斷某個節(jié)點(diǎn)的節(jié)點(diǎn)類型是否等于該節(jié)點(diǎn)類型的數(shù)值常量。
實例:節(jié)點(diǎn)類型判斷
<body> <!-- 這是一個注釋。 --> <div id="div1">這是一個div元素節(jié)點(diǎn)。</div> <script> //判斷是否為注釋節(jié)點(diǎn) var commentNode = document.body.childNodes[1]; if(commentNode.nodeType == 8){ alert('該節(jié)點(diǎn)是注釋節(jié)點(diǎn)。'); } //判斷是否為元素節(jié)點(diǎn) var divNode = document.getElementById('div1'); if(divNode.nodeType == 1){ alert('該節(jié)點(diǎn)是元素節(jié)點(diǎn)。'); } //判斷是否為文本節(jié)點(diǎn) var textNode = document.getElementById('div1').childNodes[0]; if(textNode.nodeType == 3){ alert('該節(jié)點(diǎn)是文本節(jié)點(diǎn)。'); } </script>
其實我們在獲取節(jié)點(diǎn)的子節(jié)點(diǎn)時,不使用 childNodes 屬性,而使用 children 屬性,就可以避免這一問題,因為通過 childNodes 屬性返回的是子節(jié)點(diǎn)集合,不僅包括元素節(jié)點(diǎn),而且還包括文本節(jié)點(diǎn),瀏覽器會將標(biāo)簽之間的空白默認(rèn)為文本節(jié)點(diǎn),而使用 children 屬性,則只返回元素節(jié)點(diǎn),不包括文本節(jié)點(diǎn),還不包括注釋節(jié)點(diǎn)。
節(jié)點(diǎn)名稱和節(jié)點(diǎn)值,直接使用實例演示:
<body> <!-- nodeName 和 nodeValue 演示 --> <div id="div1">節(jié)點(diǎn)名稱和節(jié)點(diǎn)值。</div> <script> //元素節(jié)點(diǎn) = nodeType:1 var divNode = document.getElementById('div1'); console.log('元素節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + divNode.nodeName + '/' + divNode.nodeValue); //nodeName:返回 元素的標(biāo)簽名(DIV)全部大寫。nodeValue:返回 null //屬性節(jié)點(diǎn) = nodeType:2 var attrNode = divNode.attributes[0]; console.log('屬性節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + attrNode.nodeName + '/' + attrNode.nodeValue); //nodeName:返回 屬性的名稱(id)。nodeValue:返回 屬性的值(div1) //文本節(jié)點(diǎn) = nodeType:3 var textNode = divNode.childNodes[0]; console.log('文本節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + textNode.nodeName + '/' + textNode.nodeValue); //nodeName:返回 #text。nodeValue:返回 節(jié)點(diǎn)所包含的文本 //(comment)注釋節(jié)點(diǎn) = nodeType:8 var commentNode = document.body.childNodes[1]; console.log('注釋節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + commentNode.nodeName + '/' + commentNode.nodeValue); //nodeName:返回 #comment。nodeValue:返回 注釋的內(nèi)容 //(DocumentType)文檔類型節(jié)點(diǎn) = nodeType:10 console.log('文檔類型節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + document.doctype.nodeName + '/' + document.doctype.nodeValue); //nodeName:返回 document的名稱(html)。nodeValue:返回 null //(DocumentFragment)文檔片段節(jié)點(diǎn) = nodeType:11 var farg = document.createDocumentFragment(); console.log('文檔片段節(jié)點(diǎn)的節(jié)點(diǎn)名稱和值:' + farg.nodeName + '/' + farg.nodeValue); //nodeName:返回 #document-fragment。nodeValue:返回 null </script>
2、DOMReady
(1)、JS 在頁面中的位置
頁面中的 JS 代碼,可以引入外部的 JS 文件,也可以放在 <head> 標(biāo)簽中或 <body> 標(biāo)簽中,放在 body 中的 JS 會在頁面加載的時候被執(zhí)行,而 head 中的 JS 會在被調(diào)用的時候才執(zhí)行。
放在 <head> 標(biāo)簽中或 <body> 標(biāo)簽中 的區(qū)別:
瀏覽器解析 HTML 文檔是從上到下、從左到右依次進(jìn)行的,如果把 JS 放在 head 里的話,則先被解析,但這時候 body 還沒有被解析,所以會返回空值,也就是會出錯。放在 head 中的 JS 代碼會在頁面加載完成之前就讀取,而放在 body 中的 JS 代碼,會在整個頁面加載完成之后讀取。這就說明了,如果我們想定義一個全局對象,而這個對象是頁面中的某個按鈕時,我們必須將其放入 body 中,道理很明顯:如果放入head,那當(dāng)你定義的時候,那個按鈕都沒有被加載,可能獲得的是一個 undefind。
腳本應(yīng)該放置的位置:
頁面中的 JS 會在瀏覽器加載頁面的時候被立即執(zhí)行,有時候我們想讓一段腳本在頁面加載的時候執(zhí)行,而有時候我們想在用戶觸發(fā)一個事件的時候執(zhí)行腳本。
需調(diào)用才執(zhí)行的腳本或事件觸發(fā)執(zhí)行的腳本放在 HTML 的 head 部分中,head 部分中的腳本,可以保證腳本在任何調(diào)用之前被加載。
當(dāng)頁面被加載時執(zhí)行的腳本放在 HTML 的 body 部分。放在 body 部分的腳本通常被用來生成頁面的內(nèi)容。
body 和 head 部分可同時有腳本:文件中可以在 body 和 head 部分同時存在腳本。
外部腳本:
有時候需要在幾個頁面中運(yùn)行同樣的腳本程序, 這時就需要用到外部腳本,而不需要在各個頁面中重復(fù)的寫這些代碼。
(2)、JS 在頁面中的應(yīng)用
①、放在 body 部分中:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>javaScript實例</title> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> <script> document.getElementById('main').style.color = 'red'; </script> </body> </html>
上面的實例,通過 JS 改變了 h1 標(biāo)題的 color 屬性,當(dāng)打開頁面時標(biāo)題顯示為紅色。
②、放在 head 部分中
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Javascript實例</title> <script> document.getElementById('main').style.color = 'red'; </script> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> </body> </html>將同樣的 JS 代碼放在 head 部分中,就無法正常運(yùn)行了,瀏覽器報錯:未捕獲的類型錯誤:不能讀取一個空元素的樣式屬性。出現(xiàn)這樣的錯誤就是沒有分清 HTML標(biāo)簽和 DOM 節(jié)點(diǎn)之間的區(qū)別,HTML 是超文本標(biāo)記語言,用于展示內(nèi)容,而行為交互是需要通過 DOM 操作來實現(xiàn)的,HTML 標(biāo)簽要通過瀏覽器解析,才能變成 DOM 節(jié)點(diǎn),當(dāng)我們向地址欄輸入一個 URL 時,開始加載頁面,然后就能夠看到內(nèi)容,在這期間就有一個 DOM 節(jié)點(diǎn)構(gòu)建的過程,節(jié)點(diǎn)是以樹的形式組織的。JS 對于 DOM 的操作必須在 DOM 樹構(gòu)建完畢后,而上面的實例,head 部分中的 JS 會被瀏覽器先解析,但是這時候 body 中的元素還沒有被解析,DOM樹并沒有構(gòu)建完畢,所以就出事了。那如果就想把 JS 代碼放在 head 部分中,還不想出錯,該怎么解決呢?下文再做具體分析。
(3)、DOMReady
HTML 標(biāo)簽需要通過瀏覽器解析才會變成 DOM 節(jié)點(diǎn),在刷新 URL 地址的時候就有 DOM 節(jié)點(diǎn)的構(gòu)建過程,當(dāng)頁面上所有的 HTML 標(biāo)簽都轉(zhuǎn)換為節(jié)點(diǎn)以后,DOM 樹才構(gòu)建完畢,這就簡稱為 DOMReady。
那瀏覽器是如何將 HTML 標(biāo)簽解析變成節(jié)點(diǎn)的呢,瀏覽器是通過渲染引擎來實現(xiàn)的,渲染引擎的職責(zé)就是渲染,即把請求的 HTML 內(nèi)容顯示到瀏覽器屏幕上。所謂渲染,就是瀏覽器把請求到的 HTML 內(nèi)容顯示在屏幕上的過程,所謂渲染引擎,就是瀏覽器的內(nèi)核,是瀏覽器最核心的東西。
各大瀏覽器的渲染引擎:
Firefox、Chrome 和 Safari 是基于兩種渲染引擎構(gòu)建的:
Firefox 使用 Geoko 內(nèi)核,是 Mozilla 自主研發(fā)的渲染引擎,Gecko 是開源引擎,Gecko 也是一個跨平臺內(nèi)核,可以在Windows、linux和Mac OS X等主要操作系統(tǒng)中運(yùn)行。
Safari 和 Chrome 都使用 WebKit 內(nèi)核,WebKit 和 WebCore 均是 KHTML 的衍生產(chǎn)品,KHTML 是 HTML 頁面渲染引擎之一,由 KDE 所開發(fā)。KHTML 擁有速度快捷的優(yōu)點(diǎn),對錯誤語法的容忍度要比 Mozilla 產(chǎn)品所使用的 Gecko 引擎小。WebKit 是一款開源渲染引擎,他本來是為 linux 平臺研發(fā)的,后來由蘋果公司移植到 Mac 及 Windows上使用。
而 IE 使用的是 Trident 內(nèi)核(又稱為MSHTML),是微軟的視窗操作系統(tǒng) (Windows) 搭載的網(wǎng)頁瀏覽器 Internet Explorer 使用的渲染引擎,該內(nèi)核程序在1997年的 IE 4 中首次被采用,之后不斷地加入新的技術(shù)并隨著新版本的 IE 發(fā)布,但是 Trident 只能用于 Windows 平臺。
渲染引擎在取得內(nèi)容之后的基本流程:
解析 HTML 以構(gòu)建 DOM 樹 -> 構(gòu)建 render 樹 -> 布局 render 樹 -> 繪制 render 樹
渲染引擎開始解析 HTML,并將標(biāo)簽轉(zhuǎn)化為一棵DOM樹,接著,他解析外部 CSS 文件及 style 標(biāo)簽中的樣式信息,這些樣式信息以及 HTML 中的可見性指令將被用來構(gòu)建另一棵樹 —— render 樹,用于渲染 DOM 樹的樹 —— 渲染樹(render tree)。
render 樹由一些包含顏色和大小等屬性的矩形組成,他將被按照正確的順序顯示到屏幕上。
render 樹構(gòu)建好了之后,將會執(zhí)行布局過程,它將確定每個節(jié)點(diǎn)在屏幕上的確切坐標(biāo)。再下一步就是繪制,即遍歷 render 樹,并使用 UI 后端層繪制每個節(jié)點(diǎn)。
值得注意的是,這個過程是逐步完成的,為了更好的用戶體驗,渲染引擎將會盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會等到所有的 HTML 都解析完成之后再去構(gòu)建和布局render 樹。他是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時,可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容。
瀏覽器打開頁面的常規(guī)流程:
①、瀏覽器下載的順序是從上到下,渲染的順序也是從上到下,下載和渲染是同時進(jìn)行的。
②、在渲染到頁面的某一部分時,其上面的所有部分都已經(jīng)下載完成(并不是說所有相關(guān)聯(lián)的元素都已經(jīng)下載完)。
③、如果遇到語義解釋性的標(biāo)簽嵌入文件(JS 腳本,CSS 樣式),那么此時IE的下載過程會啟用單獨(dú)連接進(jìn)行下載。
④、并且在下載后進(jìn)行解析,解析過程中,停止頁面所有往下元素的下載。
⑤、樣式表在下載完成后,將和以前下載的所有樣式表一起進(jìn)行解析,解析完成后,將對此前所有元素(含以前已經(jīng)渲染的)重新進(jìn)行渲染。
⑥、JS、CSS中如有重復(fù)定義,則之后定義的函數(shù)將覆蓋前面定義的函數(shù)。
3、DOMReady 實現(xiàn)
腳本在 HTML DOM 加載渲染布局顯示完成后才能運(yùn)行,且要放在 body 部分內(nèi)容的后面,而 DOMReady 則定義使腳本無論放在哪里都能執(zhí)行。
(1)、使用定時器
把 JS 代碼放在 head 部分中,還不想出錯,就是等 DOM 樹構(gòu)建完畢后,再執(zhí)行腳本,那么是否可以使用定時器延遲腳本的執(zhí)行,避免這個錯誤呢。
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>JavaScript實例</title><script>//在DomReady完畢后2秒執(zhí)行。打開頁面可以看到一個樣式變化的過程。setTimeout(function (){ document.getElementById('main').style.color = 'red';},2000);</script></head><body><h1 id="main">這是h1標(biāo)題中的一些文本。</h1></body></html>測試上面的代碼,可以看到,在打開頁面時標(biāo)題顯示為黑色,過一會后才會轉(zhuǎn)變?yōu)榧t色,雖然這樣不會報錯了,但這并不是我們想要的效果。那可以再將延遲時間縮短,不就解決了嗎,我們將時間設(shè)置為 30 毫秒再試試。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript實例</title> <script> //將事件縮至30毫秒 setTimeout(function (){ document.getElementById('main').style.color = 'red'; },30); </script> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> </body> </html>測試上面代碼,在打開頁面時顯示為紅色,但如果刷新頁面的話,還是能看到黑色的閃動了一下,雖然無傷大雅,但這樣還存在著一個很嚴(yán)重的問題,如果 DomReady 時間超過了 30 毫秒,那還是會出錯,顯然這方法是不可行的。
(2)、使用 window.onload
window.onload 事件是在瀏覽器繪制完 DOM 節(jié)點(diǎn),再加載完頁面上的所有資源后,然后執(zhí)行腳本。也就是說在文檔解析渲染,資源加載完成之前,不讓腳本執(zhí)行。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript實例</title> <script> window.onload = function (){ document.getElementById('main').style.color = 'red'; }; </script> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> </body> </html>測試上面的代碼,打開頁面后顯示為紅色,再刷新也不會出現(xiàn)黑色的閃動,顯然該方法是可行的。如果把 JS 代碼放在 head 部分中,一般情況下都需要綁定一個監(jiān)聽,即window.onload 事件,等全部的 HTML 文檔渲染完成后,再執(zhí)行代碼,這樣就妥妥的了。
該方法在文檔外部資源不多的情況下,是沒什么問題,但如果網(wǎng)站有很多圖片,我們要用 JS 做到在點(diǎn)擊每張圖片時彈出圖片的 src 屬性,這時候就有問題了,而且是出大事了,我們都知道 DOM 樹很快就構(gòu)建完成了,但是這么多圖片還在緩慢的加載中,想要先執(zhí)行 JS 的效果,就得等到所有的圖片全部加載完畢后才能實現(xiàn),而在這期間頁面不會響應(yīng)用戶任何操作,瀏覽器就跟死了一般。所以使用 window.onload 對于很多實際的操作(比如DOM操作,事件綁定等)就顯得太遲了,比如圖片過多,window.onload 卻遲遲不能觸發(fā),影響用戶體驗。而 DOMReady 就可以滿足提前綁定事件的需求。
(3)、jQuery 實現(xiàn) DOMReady
最簡單的方法就是使用 jQuery,jQuery 是一個 JS 函數(shù)庫,封裝了大量的 JS 方法,使用 jQuery 可以極大地簡化 JS 編程。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript實例</title> <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script> //jQuery 實現(xiàn) $(document).ready(function (){ document.getElementById('main').style.color = 'red'; }); </script> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> </body> </html>
(4)、JS 實現(xiàn) DOMReady
用 JS 實現(xiàn) DOMReady 其實也很簡單,可以添加一個監(jiān)聽事件,即 addEventListener,該方法的語法為:document.addEventListener("事件名稱", 函數(shù), false);,false 表示在冒泡階段捕獲。再傳入DOMContentLoaded 事件,這個事件是從 onload 事件延伸而來的,當(dāng)一個頁面完成加載時,初始化腳本的方法是使用 onload 事件,該方法的缺點(diǎn)是僅在所有資源都完全加載后才被觸發(fā),如果頁面的圖片很多的話,從用戶訪問到 onload 觸發(fā)可能需要較長的時間,所以開發(fā)人員隨后創(chuàng)建了一種自定義事件,DOMReady,他在 DOM 構(gòu)建之后、資源加載之前就可以被觸發(fā),他的表現(xiàn)形式就是 DOMContentLoaded 。jQuery 源碼中也是使用該方法完成的。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JavaScript實例</title> <script> function domReady(fn){ document.addEventListener('DOMContentLoaded', fn, false); } domReady(function (){ document.getElementById('main').style.color = 'red'; }); </script> </head> <body> <h1 id="main">這是h1標(biāo)題中的一些文本。</h1> </body> </html>
上面代碼,我們給參數(shù)傳入一個回調(diào)函數(shù),將 DOMReady 方法封裝為一個函數(shù),方便以后使用,該方法支持所有現(xiàn)代瀏覽器,但是不支持 IE9 之前的瀏覽器。
4、HTML 嵌套規(guī)則
了解 HTML 嵌套規(guī)則,是進(jìn)行 DOM 操作的基礎(chǔ)。
HTML 存在許多種類型的標(biāo)簽,有的標(biāo)簽下面只允許特定的標(biāo)簽存在,這就叫 HTML 嵌套規(guī)則。
如果不按 HTML 嵌套規(guī)則寫,瀏覽器就不會正確解析,會將不符合嵌套規(guī)則的節(jié)點(diǎn)放到目標(biāo)節(jié)點(diǎn)的下面,或者變成純文本。
所以在編寫任何代碼時,都需要按照規(guī)則編寫,有利于解析,有利于操作,有利于優(yōu)化,有利于維護(hù),有利于重構(gòu)。
HTML 元素可簡單的分為塊狀元素和內(nèi)聯(lián)元素兩類。下面是一些需要注意的嵌套規(guī)則:
①、塊元素可以包含內(nèi)聯(lián)元素或某些塊元素,但內(nèi)聯(lián)元素卻不能包含塊元素,他只能包含其他的內(nèi)聯(lián)元素,li元素內(nèi)可以包含 div 元素。
<div> <h1></h1> <p></p></div><a href=""><span class=""></span></a><ul> <li> <div></div> </li></ul>
②、塊元素不能包含在p元素內(nèi)。
<p>這是一些文本 <div style="width:100px;height:200px;border:1px solid black;"></div> </p> <p>這是一些文本 <ul> <li>蘋果</li> <li>香蕉</li> </ul> </p>測試上面的代碼,雖然他們都能被正常顯示,但是并不處在同一層。
③、有幾個特殊的塊元素只能包含內(nèi)聯(lián)元素,不能包含塊元素,這幾個特殊的塊元素是 h1-h6、p 和 dt。
④、塊元素與塊元素并列,內(nèi)聯(lián)元素與內(nèi)聯(lián)元素并列。
<div> <h2></h2> <p></p> </div> <div> <a href=""></a> <span class=""></span> </div>
5、DOM 操作
DOM 節(jié)點(diǎn)是一個非常復(fù)雜的東西,對于他的每一個屬性的訪問,有可能會向上搜尋到 n 多個原型點(diǎn),因此 DOM 操作是個很耗性能的操作。
使用 DOM 操作很復(fù)雜,所以建議盡量使用現(xiàn)成的框架來實現(xiàn)業(yè)務(wù),比如 MVVM 框架,將所有的 DOM 操作,都轉(zhuǎn)交給框架內(nèi)部做精細(xì)有效的處理。
新聞熱點(diǎn)
疑難解答