什么是DOM?胶坠?骇陈?
DOM(Document Object Model? 文檔對(duì)象模型)是針對(duì)HTML和XML文檔的一個(gè)API(應(yīng)用程序編程接口)革半。
注意,IE中的所有DOM對(duì)象都是以COM(組件對(duì)象模型)對(duì)象的形式實(shí)現(xiàn)的萧朝。IE中的DOM對(duì)象與原生JavaScript對(duì)象的行為或活動(dòng)特點(diǎn)并不一致。
COM對(duì)象是遵循COM規(guī)范編寫、以Win32動(dòng)態(tài)鏈接庫(DLL)或可執(zhí)行文件(EXE)形式發(fā)布的可執(zhí)行二進(jìn)制代碼掷伙,能夠滿足對(duì)組件架構(gòu)的所有需求。DOM定義了一個(gè)Node接口又兵,這個(gè)接口在JavaScript中是作為Node類型實(shí)現(xiàn)的任柜,而在IE8-瀏覽器中的所有DOM對(duì)象都是以COM對(duì)象的形式實(shí)現(xiàn)的。所以沛厨,IE8-瀏覽器并不支持Node對(duì)象的寫法宙地。
1.節(jié)點(diǎn)層次
首先,大家知道 “在Java的世界里逆皮,一切皆對(duì)象“宅粥,同樣,Js 也是电谣。但是秽梅,JavaScript并不具備傳統(tǒng)的面向?qū)ο笳Z言所支持的類和接口等基本結(jié)構(gòu)。DOM描繪了一個(gè)層次化的節(jié)點(diǎn)樹剿牺,允許開發(fā)人員添加企垦、移除和修改頁面的某一部分。
DOM可以將任何HTML或XML文檔描繪成一個(gè)由多層節(jié)點(diǎn)構(gòu)成的結(jié)構(gòu)晒来。節(jié)點(diǎn)分為幾種不同的類型钞诡,每種類型分別表示文檔中不同的信息及(或)標(biāo)記。每個(gè)節(jié)點(diǎn)都擁有各自的特點(diǎn)湃崩、數(shù)據(jù)和方法荧降,另外也與其他節(jié)點(diǎn)存在某種關(guān)系。節(jié)點(diǎn)之間的關(guān)系構(gòu)成了層次攒读,而所有頁面標(biāo)記則表現(xiàn)為一個(gè)一特定節(jié)點(diǎn)為根結(jié)點(diǎn)的樹形結(jié)構(gòu)誊抛。
以HTML為例:
文檔節(jié)點(diǎn)是每個(gè)文檔的根結(jié)點(diǎn)。在這個(gè)例子中整陌,文檔節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn)拗窃,即<html>元素瞎领,我們稱之為文檔元素。
文檔元素是文檔的最外元素随夸,文檔中的其他所有元素都包含在文檔元素中九默。每個(gè)文檔只能有一個(gè)文檔元素。在HTML頁面中宾毒,文檔元素始終都是<html>元素驼修。
每一段 標(biāo)記都可以通過樹中的一個(gè)節(jié)點(diǎn)來表示:HTML元素通過元素節(jié)點(diǎn)表示,特性通過特性節(jié)點(diǎn)表示诈铛,文檔類型通過文檔類型節(jié)點(diǎn)表示乙各,而注釋則通過注釋節(jié)點(diǎn)表示。
每個(gè)方框是文檔的節(jié)點(diǎn)幢竹,它表示一個(gè)Node對(duì)象耳峦。樹形的根部Document節(jié)點(diǎn),它代表整個(gè)文檔焕毫。
2.節(jié)點(diǎn)類型
元素節(jié)點(diǎn)? ? ? ? ? ? ? ? ? ? ?Node.ELEMENT_NODE(1)
屬性節(jié)點(diǎn)? ? ? ? ? ? ? ? ? ? ?Node.ATTRIBUTE_NODE(2)
文本節(jié)點(diǎn)? ? ? ? ? ? ? ? ? ? ?Node.TEXT_NODE(3)
CDATA節(jié)點(diǎn) ? ? ? ? ? ? ? ? ? ? ? ? Node.CDATA_SECTION_NODE(4)
實(shí)體引用名稱節(jié)點(diǎn)? ? ? ?Node.ENTRY_REFERENCE_NODE(5)
實(shí)體名稱節(jié)點(diǎn)? ? ? ? ? ? ? Node.ENTITY_NODE(6)
處理指令節(jié)點(diǎn)? ? ? ? ? ? ? Node.PROCESSING_INSTRUCTION_NODE(7)
注釋節(jié)點(diǎn)? ? ? ? ? ? ? ? ? ? ? ? ?Node.COMMENT_NODE(8)
文檔節(jié)點(diǎn)? ? ? ? ? ? ? ? ? ? ? ? ?Node.DOCUMENT_NODE(9)
文檔類型節(jié)點(diǎn)? ? ? ? ? ? ? Node.DOCUMENT_TYPE_NODE(10)
文檔片段節(jié)點(diǎn)? ? ? ? ? ? ? Node.DOCUMENT_FRAGMENT_NODE(11)
DTD聲明節(jié)點(diǎn) ? ? ? ? ? ? ? ? ? ? ?Node.NOTATION_NODE(12)
Document
Document表示文檔蹲坷,在瀏覽器中,document對(duì)象是HTMLDocument的一個(gè)實(shí)例邑飒,表示整個(gè)頁面循签,它同時(shí)也是window對(duì)象的一個(gè)屬性。Document有下面的特性:
(1)nodeType為9
(2)nodeName為#document
(3)nodeValue為null
(4)parentNode為null
(5)子節(jié)點(diǎn)可能是一個(gè)DocumentType或Element
Element
Element提供了對(duì)元素標(biāo)簽名疙咸,子節(jié)點(diǎn)和特性的訪問县匠,我們常用HTML元素比如div,span撒轮,a等標(biāo)簽就是element中的一種聚唐。
Element有下面幾條特性:
(1)nodeType為1
(2)nodeName為元素標(biāo)簽名,tagName也是返回標(biāo)簽名
(3)nodeValue為null
(4)parentNode可能是Document或Element
(5)子節(jié)點(diǎn)可能是Element腔召,Text杆查,Comment,Processing_Instruction臀蛛,CDATASection或EntityReference
Text
Text表示文本節(jié)點(diǎn)亲桦,它包含的是純文本內(nèi)容,不能包含html代碼浊仆,但可以包含轉(zhuǎn)義后的html代碼客峭。Text有下面的特性:
(1)nodeType為3
(2)nodeName為#text
(3)nodeValue為文本內(nèi)容
(4)parentNode是一個(gè)Element
(5)沒有子節(jié)點(diǎn)
Attr
Attr類型表示元素的特性,相當(dāng)于元素的attributes屬性中的節(jié)點(diǎn)抡柿,它有下面的特性:
(1)nodeType值為2
(2)nodeName是特性的名稱
(3)nodeValue是特性的值
(4)parentNode為null
Comment
Comment表示HTML文檔中的注釋舔琅,它有下面的幾種特征:
(1)nodeType為8
(2)nodeName為#comment
(3)nodeValue為注釋的內(nèi)容
(4)parentNode可能是Document或Element
(5)沒有子節(jié)點(diǎn)
DocumentFragment類型
DocumentFragment是所有節(jié)點(diǎn)中唯一一個(gè)沒有對(duì)應(yīng)標(biāo)記的類型,它表示一種輕量級(jí)的文檔洲劣,可能當(dāng)作一個(gè)臨時(shí)的倉(cāng)庫用來保存可能會(huì)添加到文檔中的節(jié)點(diǎn)备蚓。DocumentFragment有下面的特性:
(1)nodeType為11
(2)nodeName為#document-fragment
(3)nodeValue為null
(4)parentNode為null
節(jié)點(diǎn)創(chuàng)建型
createElement
createElement通過傳入指定的一個(gè)標(biāo)簽名來創(chuàng)建一個(gè)元素课蔬,如果傳入的標(biāo)簽名是一個(gè)未知的,則會(huì)創(chuàng)建一個(gè)自定義的標(biāo)簽郊尝,注意:IE8以下瀏覽器不支持自定義標(biāo)簽二跋。
使用如下:
var?div?=?document.createElement("div");
使用createElement要注意:通過createElement創(chuàng)建的元素并不屬于html文檔,它只是創(chuàng)建出來流昏,并未添加到html文檔中扎即,要調(diào)用appendChild或insertBefore等方法將其添加到HTML文檔樹中。
createTextNode大致同上
createTextNode接收一個(gè)參數(shù)况凉,這個(gè)參數(shù)就是文本節(jié)點(diǎn)中的文本谚鄙,和createElement一樣,創(chuàng)建后的文本節(jié)點(diǎn)也只是獨(dú)立的一個(gè)節(jié)點(diǎn)刁绒,同樣需要appendChild將其添加到HTML文檔樹中
cloneNode
cloneNode是用來返回調(diào)用方法的節(jié)點(diǎn)的一個(gè)副本闷营,它接收一個(gè)boolean參數(shù),用來表示是否復(fù)制子元素膛锭,使用如下:
var?parent?=?document.getElementById("parentElement");
var?parent2?=?parent.cloneNode(true);// 傳入true
?parent2.id?=?"parent2";
這段代碼通過cloneNode復(fù)制了一份parent元素粮坞,其中cloneNode的參數(shù)為true蚊荣,表示parent的子節(jié)點(diǎn)也被復(fù)制初狰,如果傳入false,則表示只復(fù)制了parent節(jié)點(diǎn)互例。
這段代碼很簡(jiǎn)單奢入,主要是綁定button事件,事件內(nèi)容是復(fù)制了一個(gè)parent媳叨,修改其id腥光,然后添加到文檔中。這里有幾點(diǎn)要注意:
(1)和createElement一樣糊秆,cloneNode創(chuàng)建的節(jié)點(diǎn)只是游離有html文檔外的節(jié)點(diǎn)武福,要調(diào)用appendChild方法才能添加到文檔樹中
(2)如果復(fù)制的元素有id,則其副本同樣會(huì)包含該id痘番,由于id具有唯一性捉片,所以在復(fù)制節(jié)點(diǎn)后必須要修改其id
(3)調(diào)用接收的bool參數(shù)最好傳入,如果不傳入該參數(shù)汞舱,不同瀏覽器對(duì)其默認(rèn)值的處理可能不同
除此之外伍纫,我們還有一個(gè)需要注意的點(diǎn):如果被復(fù)制的節(jié)點(diǎn)綁定了事件,則副本也會(huì)跟著綁定該事件嗎昂芜?這里要分情況討論:(1)如果是通過addEventListener或者比如onclick進(jìn)行綁定事件莹规,則副本節(jié)點(diǎn)不會(huì)綁定該事件(2)如果是內(nèi)聯(lián)方式綁定比如<div onclick="showParent()"></div>這樣的話,副本節(jié)點(diǎn)同樣會(huì)觸發(fā)事件泌神。
createDocumentFragment
createDocumentFragment方法用來創(chuàng)建一個(gè)DocumentFragment良漱。前面說到的DocumentFragment表示一種輕量級(jí)的文檔,它的作用主要是存儲(chǔ)臨時(shí)的節(jié)點(diǎn)用來準(zhǔn)備添加到文檔中债热。
創(chuàng)建型 總結(jié)
創(chuàng)建型API主要包括createElement嫉髓、createTextNode观腊、cloneNode、和createDocumentFragment四個(gè)方法算行,需要注意下面幾點(diǎn):
(1)它們創(chuàng)建的節(jié)點(diǎn)只是一個(gè)孤立的節(jié)點(diǎn)梧油,要通過appendChild添加到文檔中。
(2)cloneNode要注意如果被復(fù)制的節(jié)點(diǎn)是否包含子節(jié)點(diǎn)以及事件綁定等問題
(3)使用createDocumentFragment來解決添加大量節(jié)點(diǎn)時(shí)的性能問題
修改型
修改頁面內(nèi)容的API主要包括:appendChild州邢,insertBefore儡陨,removeChild,replaceChild量淌。
appendChild
appendChild我們?cè)谇懊嬉呀?jīng)用到多次骗村,就是將指定的節(jié)點(diǎn)添加到調(diào)用該方法的節(jié)點(diǎn)的子元素的末尾。調(diào)用方法如下:parent.appendChild(child);child節(jié)點(diǎn)將會(huì)作為parent節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)?這個(gè)方法很簡(jiǎn)單呀枢,但是有一點(diǎn)需要注意:如果被添加的節(jié)點(diǎn)是一個(gè)頁面中存在的節(jié)點(diǎn)胚股,則執(zhí)行后這個(gè)節(jié)點(diǎn)將會(huì)添加到指定位置,其原本所在的位置將移除該節(jié)點(diǎn)裙秋,也就是說不會(huì)同時(shí)存在兩個(gè)該節(jié)點(diǎn)在頁面上琅拌,相當(dāng)于把這個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)地方。
insertBefore
insertBefore用來添加一個(gè)節(jié)點(diǎn)到一個(gè)參照節(jié)點(diǎn)之前,用法如下:
parentNode.insertBefore(newNode,refNode);
parentNode表示新節(jié)點(diǎn)被添加后的父節(jié)點(diǎn)
newNode表示要添加的節(jié)點(diǎn)
refNode表示參照節(jié)點(diǎn)隶校,新節(jié)點(diǎn)會(huì)添加到這個(gè)節(jié)點(diǎn)之前
關(guān)于第二個(gè)參數(shù)參照節(jié)點(diǎn)還有幾個(gè)注意的地方:
(1)refNode是必傳的漏益,如果不傳該參數(shù)會(huì)報(bào)錯(cuò)
(2)如果refNode是undefined或null,則insertBefore會(huì)將節(jié)點(diǎn)添加到子元素的末尾
removeChild
removeChild顧名思義深胳,就是刪除指定的子節(jié)點(diǎn)并返回绰疤,用法如下:
var?deletedChild?=?parent.removeChild(node);
deletedChild指向被刪除節(jié)點(diǎn)的引用,它等于node舞终,被刪除的節(jié)點(diǎn)仍然存在于內(nèi)存中轻庆,可以對(duì)其進(jìn)行下一步操作。
注意:如果被刪除的節(jié)點(diǎn)不是其子節(jié)點(diǎn)敛劝,則程序?qū)?huì)報(bào)錯(cuò)余爆。我們可以通過下面的方式來確保可以刪除:
if(node.parentNode){
node.parentNode.removeChild(node);
}
通過節(jié)點(diǎn)自己獲取節(jié)點(diǎn)的父節(jié)點(diǎn)夸盟,然后將自身刪除蛾方。
replaceChild
replaceChild用于使用一個(gè)節(jié)點(diǎn)替換另一個(gè)節(jié)點(diǎn),用法如下:parent.replaceChild(newChild,oldChild);
newChild是替換的節(jié)點(diǎn)上陕,可以是新的節(jié)點(diǎn)桩砰,也可以是頁面上的節(jié)點(diǎn),如果是頁面上的節(jié)點(diǎn)释簿,則其將被轉(zhuǎn)移到新的位置亚隅,oldChild是被替換的節(jié)點(diǎn)。
頁面修改型api主要是這四個(gè)接口辕万,要注意幾個(gè)特點(diǎn):
(1)不管是新增還是替換節(jié)點(diǎn)枢步,如果新增或替換的節(jié)點(diǎn)是原本存在頁面上的沉删,則其原來位置的節(jié)點(diǎn)將被移除渐尿,也就是說同一個(gè)節(jié)點(diǎn)不能存在于頁面的多個(gè)位置
(2)節(jié)點(diǎn)本身綁定的事件不會(huì)消失,會(huì)一直保留著矾瑰。
節(jié)點(diǎn)查詢型API
document.getElementById
這個(gè)接口很簡(jiǎn)單砖茸,根據(jù)元素id返回元素,返回值是Element類型殴穴,如果不存在該元素凉夯,則返回null。?使用這個(gè)接口有幾點(diǎn)要注意:
(1)元素的Id是大小寫敏感的采幌,一定要寫對(duì)元素的id
(2)HTML文檔中可能存在多個(gè)id相同的元素劲够,則返回第一個(gè)元素
(3)只從文檔中進(jìn)行搜索元素,如果創(chuàng)建了一個(gè)元素并指定id休傍,但并沒有添加到文檔中征绎,則這個(gè)元素是不會(huì)被查找到的
document.getElementsByTagName
這個(gè)接口根據(jù)元素標(biāo)簽名獲取元素,返回一個(gè)即時(shí)的HTMLCollection類型磨取,什么是即時(shí)的HTMLCollection類型呢人柿?
這個(gè)方法有幾點(diǎn)要注意:
(1)如果要對(duì)HTMLCollection集合進(jìn)行循環(huán)操作柴墩,最好將其長(zhǎng)度緩存起來,因?yàn)槊看窝h(huán)都會(huì)去計(jì)算長(zhǎng)度凫岖,暫時(shí)緩存起來可以提高效率
(2)如果沒有存在指定的標(biāo)簽江咳,該接口返回的不是null,而是一個(gè)空的HTMLCollection
(3)“ * ”表示所有標(biāo)簽
document.getElementsByName
getElementsByName主要是通過指定的name屬性來獲取元素,它返回一個(gè)即時(shí)的NodeList對(duì)象呵哨。使用這個(gè)接口主要要注意幾點(diǎn):
(1)返回對(duì)象是一個(gè)即時(shí)的NodeList赁濒,它是隨時(shí)變化的
(2)在HTML元素中,并不是所有元素都有name屬性孟害,比如div是沒有name屬性的拒炎,但是如果強(qiáng)制設(shè)置div的name屬性,它也是可以被查找到的
(3)在IE中挨务,如果id設(shè)置成某個(gè)值击你,然后傳入getElementsByName的參數(shù)值和id值一樣,則這個(gè)元素是會(huì)被找到的谎柄,所以最好不好設(shè)置同樣的值給id和name
擴(kuò)展
一.選擇符API
1.querySelector()方法
該方法接收一個(gè)CSS選擇符丁侄,返回與該模式匹配的第一個(gè)元素,如果沒有找到匹配的元素朝巫,返回null鸿摇。
通過Document類型調(diào)用querySelector()方法時(shí),會(huì)在文檔元素的范圍內(nèi)查找匹配的元素劈猿。而通過Element類型調(diào)用querySelector()方法時(shí)拙吉,只會(huì)在該元素后代元素的范圍內(nèi)查找匹配的元素。
注:眾多JavaScript庫中最常用的一項(xiàng)功能揪荣,就是根據(jù)CSS選擇符選擇與某個(gè)模式匹配的DOM元素筷黔。實(shí)際上,jQuery的核心就是通過CSS選擇符查詢DOM文檔取得元素的引用仗颈,從而拋開了getElementById()和getElementByTagName()佛舱。
CSS選擇符可以簡(jiǎn)單也可以復(fù)雜,視情況而定。如果傳入了不被支持的選擇符名眉,querySelector()會(huì)拋出錯(cuò)誤粟矿。
2.querySelectorAll()
querySelectorAll()方法接受的參數(shù)與querySelector()方法一樣,都是一個(gè)CSS選擇符损拢,但是返回的不僅不僅是一個(gè)元素陌粹,而是一個(gè)NodeList的實(shí)例。
與querySlector()一樣能夠調(diào)querySlectorAll()方法的有document福压,element掏秩,DocumentFragment。
要取得返回的NodeList中的每一個(gè)元素荆姆,可以使用item()方法蒙幻,也可以使用方括號(hào)語法,比如:
getElementsByClassName()
1)使用方法:element.getElementsByClassName("classNames")胆筒,其中邮破,element是有效的DOM元素(包括document) ? ? ? ?
classNames是CSS類名稱的組合(多個(gè)類名之間用空格,可以是多個(gè)空格隔開)仆救,如
element.getElementsByClassName("class2 class1")
將選取elements后代元素中同時(shí)應(yīng)用了class1和class2樣式的元素(樣式名稱不區(qū)分先后順序) ? ?2)說明:a. 返回值是一個(gè)nodeList集合(區(qū)別于Array? 有明確的定義與概念)
? ? ? ? ? ? ? ? ? b. 該方法只能選取調(diào)用該方法的元素的后代元素抒和。
?3)兼容性:IE8及其以下版本的瀏覽器未實(shí)現(xiàn)getElementsByClassName方法
HTML5添加的getElementsByClassName()方法,可以通過Document對(duì)象及所有HTML元素調(diào)用該方法
事件
所謂事件彤蔽,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互瞬間
一摧莽、事件流
在頁面上,單擊某個(gè)元素的同時(shí)顿痪,也單擊了它的包含容器镊辕。事件流就是描述的從頁面中接收事件的順序。IE是事件冒泡流蚁袭,Netscape是事件捕獲流征懈。
事件冒泡
事件開始時(shí),由最具體的元素(文檔中嵌套最深的那個(gè)節(jié)點(diǎn))接收撕阎,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)(文檔)受裹;(所有現(xiàn)代瀏覽器都支持事件冒泡) 如果單擊了頁面中的<div>元素碌补,那么這個(gè)click事件會(huì)按照如下順序傳播:
(1)<div>
(2)<body>
(3)<html>
(4)document
事件捕獲
不太具體的節(jié)點(diǎn)最早接收到事件虏束,最具體的節(jié)點(diǎn)最后接收到事件。(老版本瀏覽器不支持)如果單擊了頁面中的<div>元素厦章,那么這個(gè)click事件會(huì)按照如下順序傳播:
(1)document
(2)<html>
(3)<body>
(4)<div>
二镇匀、DOM事件流
DOM2級(jí)事件規(guī)定的事件流包括三個(gè)階段:
1、事件捕獲階段
2袜啃、處于目標(biāo)階段
3汗侵、事件冒泡階段
在DOM事件流中,實(shí)際的目標(biāo)(div元素)在捕獲階段不會(huì)接受到事件。這意味著在捕獲階段晰韵,事件從document到<html>再到<body>后就停止了发乔。下一階段是”處于目標(biāo)”階段,于是事件在<div>上發(fā)生雪猪,并在事件處理中被看成冒泡階段的一部分栏尚。然后,冒泡階段發(fā)生只恨,事件又傳播回文檔译仗。
多數(shù)支持DOM事件流的瀏覽器都實(shí)現(xiàn)了一種特定的行為:即使”DOM2級(jí)事件”規(guī)范明確要求捕獲階段不會(huì)涉及事件目標(biāo),但I(xiàn)E9官觅、Safari纵菌、Chrome、Firefox和Opera9.5及更高版本都會(huì)在捕獲階段觸發(fā)事件對(duì)象上的事件休涤。結(jié)果咱圆,就是有兩個(gè)機(jī)會(huì)在目標(biāo)對(duì)象上面操作事件。
瀏覽器的內(nèi)核
主要分成兩部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎功氨。?渲染引擎:負(fù)責(zé)取得網(wǎng)頁的內(nèi)容(HTML闷堡、XML、圖像等等)疑故、整理訊息(例如加入CSS等)杠览,以及計(jì)算網(wǎng)頁的顯示方式,然后會(huì)輸出至顯示器或打印機(jī)纵势。瀏覽器的內(nèi)核的不同對(duì)于網(wǎng)頁的語法解釋會(huì)有不同踱阿,所以渲染的效果也不相同。所有網(wǎng)頁瀏覽器钦铁、電子郵件客戶端以及其它需要編輯软舌、顯示網(wǎng)絡(luò)內(nèi)容的應(yīng)用程序都需要內(nèi)核。?JS引擎則:解析和執(zhí)行javascript來實(shí)現(xiàn)網(wǎng)頁的動(dòng)態(tài)效果牛曹。?最開始渲染引擎和JS引擎并沒有區(qū)分的很明確佛点,后來JS引擎越來越獨(dú)立,內(nèi)核就傾向于只指渲染引擎黎比。
解析html以構(gòu)建dom樹 -> 構(gòu)建render樹 -> 布局render樹 -> 繪制render樹超营。
當(dāng)瀏覽器獲得一個(gè)html文件時(shí),會(huì)“自上而下”加載阅虫,并在加載過程中進(jìn)行解析渲染演闭。?
解析:?
1. 瀏覽器會(huì)將HTML解析成一個(gè)DOM樹,DOM 樹的構(gòu)建過程是一個(gè)深度遍歷過程:當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)都構(gòu)建好后才會(huì)去構(gòu)建當(dāng)前節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn)颓帝。?
2. 將CSS解析成 CSS Rule Tree 米碰。?
3. 根據(jù)DOM樹和CSSOM來構(gòu)造 Rendering Tree窝革。
注意:Rendering Tree 渲染樹并不等同于 DOM 樹,因?yàn)橐恍┫?Header 或 display:none 的東西就沒必要放在渲染樹中了吕座。
4.有了Render Tree虐译,瀏覽器已經(jīng)能知道網(wǎng)頁中有哪些節(jié)點(diǎn)、各個(gè)節(jié)點(diǎn)的CSS定義以及他們的從屬關(guān)系吴趴。下一步操作稱之為L(zhǎng)ayout菱蔬,顧名思義就是計(jì)算出每個(gè)節(jié)點(diǎn)在屏幕中的位置。?
5.再下一步就是繪制史侣,即遍歷render樹拴泌,并使用UI后端層繪制每個(gè)節(jié)點(diǎn)。
上述過程是逐步完成的惊橱,為了更好的用戶體驗(yàn)蚪腐,渲染引擎將會(huì)盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會(huì)等到所有的html都解析完成之后再去構(gòu)建和布局render樹税朴。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容回季,同時(shí),可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容正林。
(1)Reflow(回流):瀏覽器要花時(shí)間去渲染泡一,當(dāng)它發(fā)現(xiàn)了某個(gè)部分發(fā)生了變化影響了布局,那就需要倒回去重新渲染觅廓。?
(2)Repaint(重繪):如果只是改變了某個(gè)元素的背景顏色鼻忠,文字顏色等,不影響元素周圍或內(nèi)部布局的屬性杈绸,將只會(huì)引起瀏覽器的repaint帖蔓,重畫某一部分。
Reflow要比Repaint更花費(fèi)時(shí)間瞳脓,也就更影響性能塑娇。所以在寫代碼的時(shí)候,要盡量避免過多的Reflow劫侧。
reflow的原因:
(1)頁面初始化的時(shí)候埋酬;?
(2)操作DOM時(shí);?
(3)某些元素的尺寸變了烧栋;?
(4)如果 CSS 的屬性發(fā)生變化了写妥。
減少 reflow/repaint
(1)不要一條一條地修改 DOM 的樣式。與其這樣劲弦,還不如預(yù)先定義好 css 的 class耳标,然后修改 DOM 的 className。?
(2)不要把 DOM 結(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量邑跪。?
(3)為動(dòng)畫的 HTML 元件使用 fixed 或 absoult 的 position次坡,那么修改他們的 CSS 是不會(huì) reflow 的。?
(4)千萬不要使用 table 布局画畅。因?yàn)榭赡芎苄〉囊粋€(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局砸琅。
編寫CSS時(shí)應(yīng)該注意:
CSS選擇符是從右到左進(jìn)行匹配的。從右到左轴踱,所以症脂,#nav li 我們以為這是一條很簡(jiǎn)單的規(guī)則,秒秒鐘就能匹配到想要的元素淫僻,但是诱篷,但是,但是雳灵,是從右往左匹配啊棕所,所以,會(huì)去找所有的li悯辙,然后再去確定它的父元素是不是#nav琳省。,因此躲撰,寫css的時(shí)候需要注意:
(1)dom深度盡量淺针贬。
(2)減少inline javascript、css的數(shù)量拢蛋。
(3)使用現(xiàn)代合法的css屬性桦他。
(4)不要為id選擇器指定類名或是標(biāo)簽,因?yàn)閕d可以唯一確定一個(gè)元素谆棱。
(5)避免后代選擇符瞬铸,盡量使用子選擇符。
原因:子元素匹配符的概率要大于后代元素匹配符础锐。后代選擇符;#tp p{} 子選擇符:#tp>p{}
(6)避免使用通配符嗓节,舉一個(gè)例子:?
.mod .hd *{font-size:14px;}
?根據(jù)匹配順序,將首先匹配通配符,也就是說先匹配出通配符,然后匹配.hd(就是要對(duì)dom樹上的所有節(jié)點(diǎn)進(jìn)行遍歷他的父級(jí)元素),然后匹配.mod,這樣的性能耗費(fèi)可想而知.