瀏覽器在構造DOM樹的同時也在構造着另外一棵樹-Render Tree,與DOM樹相對應暫且叫它Render樹吧,咱們知道DOM樹爲javascript提供了一些列的訪問接口(DOM API),但這棵樹是不對外的。它的主要做用就是把HTML按照必定的佈局與樣式顯示出來,用到了CSS的相關知識。從MVC的角度來講,能夠將render樹當作是V,dom樹當作是M,C則是具體的調度者,比HTMLDocumentParser等。javascript
新概念Render樹css
每個Render樹的節點稱之爲renderer或者render object,查看WEBKIT的源代碼咱們能夠發現Renderer一個基礎的類定義,這個類是全部renderer對象的基類。html
class RenderObject{ virtual void layout(); virtual void paint(PaintInfo); virtual void rect repaintRect(); Node* node; //the DOM node RenderStyle* style; // the computed style RenderLayer* containgLayer; //the containing z-index layer }
從中咱們能夠發現renderer包含了一個dom對象以及爲其計算好的樣式規則,提供了佈局以及顯示方法。具體效果圖以下:(firefox的Frames對應renderers,content對應dom)java
具體顯示的時候,每個renderer體現了一個矩形區塊的東西,即咱們常說的CSS盒子模型的概念,它自己包含了一些幾何學相關的屬性,如 寬度width,高度height,位置position等。每個renderer還有一個很重要的屬性,就是如何顯示它,display。咱們知道元 素的display有不少種,常見的就有none,inline,block,inline-block....,不一樣的display它們之間到底有啥 不一樣呢?咱們看一下代碼:node
RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
Document* doc = node->document(); RenderArena* arena = doc->renderArena(); ... RenderObject* o = 0; switch (style->display()) { case NONE: break; case INLINE: o = new (arena) RenderInline(node); break; case BLOCK: o = new (arena) RenderBlock(node); break; case INLINE_BLOCK: o = new (arena) RenderBlock(node); break; case LIST_ITEM: o = new (arena) RenderListItem(node); break; ... } return o; }
更詳細的可見WEBKIT源碼了,上面只是列出了片斷。web
DOM樹與Render樹算法
能夠這麼說,沒有DOM樹就沒有Render樹,可是它們之間可不是簡單的一對一的關係。咱們已經知道了 render樹是用於顯示的,那不可見的元素固然不會在這棵樹中出現了,譬如<header>,您還能想到哪些呢?除此以外,diplay等 於none的也不會被顯示在這棵樹裏頭,可是visibility等於hidden的元素是會顯示在這棵樹裏頭的,能夠本身想一下爲何。說了這麼多 render樹,咱們還沒見一下它的真容呢,它到底會是個什麼模樣呢?咱們看一下圖。瀏覽器
與DOM對象類型很豐富啊,什麼head,title,div,而Render樹相對來講就比較單一了,畢竟它的職責就是爲了之後的顯示渲染用 嘛。從上圖咱們還能夠看出,有些DOM元素沒有對應的renderer,而有些DOM元素卻對應了好幾個renderer,對應多個renderer的情 況是廣泛存在的,就是爲了解決一個renderer描述不清楚如何顯示出來的問題,譬如select元素,咱們就須要三個renderer,one for the display area, one for the drop down list box and one for the button。app
上圖中還有一種關係未可看出,即renderer與dom元素的位置也多是不同的。說的就是那些添加了float:ETC或者position:absolute的元素,由於它們脫離了正常的文檔流順序,構造Render樹的時候會針對它們實際的位置進行構造。dom
DOM樹可能會被咱們隨時更新,不只限於解析階段,譬如$elment.append啦或 者$elment.addClass啦,咱們看到頁面當即進行了顯示刷新,瀏覽器針對這種狀況進行了相關處理。Dom樹的根節點咱們知道是 doument,Render樹的根節點不一樣瀏覽器可能有不一樣的叫法,webkit叫它RenderView,firefox叫它ViewPortFrame。
CSS的解析
CSS用到的全部詞彙定義規範以下:
comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/
num [
0
-9
]+|[
0
-9
]*
"."
[
0
-9
]+
nonascii [\
200
-\
377
]
nmstart [_a-z]|{nonascii}|{escape}
nmchar [_a-z
0
-9
-]|{nonascii}|{escape}
name {nmchar}+
ident {nmstart}{nmchar}*
|
注:ident表明樣式中的class,name表明樣式中的id。
CSS用到的語法BNF格式的定義以下:
ruleset
: selector [
','
S* selector ]*
'{'
S* declaration [
';'
S* declaration ]*
'}'
S*
;
selector
: simple_selector [ combinator selector | S+ [ combinator selector ] ]
;
simple_selector
: element_name [ HASH | class | attrib | pseudo ]*
| [ HASH | class | attrib | pseudo ]+
;
class
:
'.'
IDENT
;
element_name
: IDENT |
'*'
;
attrib
:
'['
S* IDENT S* [ [
'='
| INCLUDES | DASHMATCH ] S*
[ IDENT | STRING ] S* ]
']'
;
pseudo
:
':'
[ IDENT | FUNCTION S* [IDENT S*]
')'
]
;
|
樣式計算
每一個HTML元素上,咱們可能定義了不少不一樣類型的樣式,如字體啦,顏色啦,佈局啦等等。即便元素上不被咱們定義樣式,瀏覽器或者用戶個性設置也會爲它默認創造一些樣式。
樣式計算一項極其複雜的過程,咱們定義樣式的時候能夠採用相似類的定義方式爲一批元素設置樣式,可是解析構造renderer的時候,瀏覽器是 爲每個構造樣式定義的。咱們可能定義了極其多的樣式並且有各類不一樣的規則,那找到元素匹配的樣式規則是挺困難的。瀏覽器有多重算法錯誤來實現計算工做, 具體就不細分析了,一個元素最終通過計算可能匹配到了不少條樣式規則,他們之間存在必定的優先順序,從低到高有:
更詳細的優先計算公式
具體可見http://www.w3.org/TR/CSS2/cascade.html#specificity
舉例說明
* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */ li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */ li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */ h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */ ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */ li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */ #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */ style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
佈局
上面肯定了renderer的樣式規則後,而後就是重要的顯示因素佈局了。當renderer構造出來並添加到render樹上以後,它並無位置跟大小信息,爲它肯定這些信息的過程,咱們就稱之爲佈局。HTML採用了一種流式佈局的佈局模型,從上到下,從左到右順序佈局,佈局的起點是從render樹的根節點開始的,對應dom樹的document節點,其初始位置爲0,0,詳細的佈局過程爲: 每一個renderer的寬度由父節點的renderer肯定。 父節點遍歷子節點,肯定子節點的位置(x,y),調用子節點的layout方法肯定其高度。 父節點根據子節點的height,margin,padding肯定自身的自身的高度。
爲了不由於局部小範圍的DOM修改或者樣式改變引發整個頁面總體的佈局從新構造,瀏覽器採用了一種dirty bit system的技術,使其儘量的只改變元素自己或者包含的子元素的佈局。固然有些狀況無可避免的要從新構造整個頁面的佈局,如適合於總體的樣式的改變影響了全部renderer,如body{font-size:111px} 字體大小發生了改變,還有一種狀況就是瀏覽器窗口進行了調整,resize。 對於界面設計來講,一個頁面最難搞的應該就是排版佈局了,內容也比較多,咱們下文進行說明From:http://www.cnblogs.com/luluping/archive/2013/04/05/3000460.html