從webkit內核簡單看css樣式和css規則優先級(權重)

webkit中樣式相關類及類間關係

資料來源: 《webkit技術內幕》css

結構相關類:html

1.StyleRuleBase類: 單個的樣式規則(選擇器+規則體)java

2.StyleSheetContents類: 樣式規則集,其成員-m_childRules是一個StyleRuleBase實例的列表,是1:n的數量關係git

3.CSSStyleSheet類: 成員-m_contents是一個StyleSheetContents實例,是1:1的數量關係github

4.DocumentStyleSheetCollection類: 多種來源的CSSStyleSheet實例(用戶樣式表,網頁做者樣式表,默認樣式表)的歸類列表web

5.Document類:數組

  • 成員-m_styleSheetCollection: DocumentStyleSheetCollection的實例,數量關係1:1
  • 成員-m_styleResover: 匹配css規則的相關類StyleSheetResover的實例,數量關係1:1

規則匹配處理相關類:瀏覽器

1.RuleSet類: 單個css樣式表經css解釋器以後的結果集,而且按照關鍵選擇器的類型分類函數

  • 成員-m_idRules: 該樣式表中的id類型規則
  • 成員-m_classRules: 該樣式表中的class類型規則
  • 成員-m_tagRules: 該樣式表中的標籤類型規則
  • 成員-m_features: 該樣式表中的其餘特徵類型規則

2.DocumentRuleSets類: 各類來源的樣式表的合併(用戶樣式表/做者樣式表/默認樣式表)

  • 成員-m_authorStyle: Ruleset對象的列表,表明做者樣式表
  • 成員-m_userStyle: Ruleset對象的列表,表明用戶樣式表

3.StyleSheetResover類: 規則匹配的主要負責類

  • 成員-m_ruleSets: DocumentRuleSets類的實例

樣式規則匹配

樣式規則匹配大體過程

1.StyleResover類以ElementRuleCollector類爲工具,爲指定的元素匹配規則,並將匹配結果保存到RenderStyle對象中,RenderStyle對象由負責該元素渲染的RenderObject對象管理和使用

2.匹配的輸入: StyleResover類中保存的css解釋器的解釋和分類完畢的樣式規則

3.匹配用到的工具類: ElementRuleCollector類

4.被匹配對象: 指定html元素

5.匹配結果: 保存至RenderStyle對象並被RenderObject用於元素渲染

規則匹配的詳細過程

說明:

1.關鍵選擇器: 樣式規則的選擇器字符串中的最右邊選擇器,它是樣式規則在解釋後分類的依據

2.匹配過程:

  • matchAllRules: 匹配規則的最外層函數,包含匹配默認,用戶和做者樣式並收集和排序已匹配規則
  • matchUARules: 匹配默認樣式
  • matchUserRules: 匹配用戶樣式
  • matchAuthorRules: 匹配做者樣式

3.做者樣式匹配詳細過程:(默認樣式,用戶樣式的匹配相似)

匹配規則: 從右向左依次匹配選擇器

前面咱們已經知道解釋以後的樣式規則是分類存放在對象中的,而爲當前元素匹配規則時,也是按id,class,tag,feature順序依次匹配.

下面咱們用僞代碼來解釋匹配的詳細過程:(僞代碼不是真正合理的和實際的代碼)

1.重要參數

//參數e: 指代被匹配的元素
//重要的變量
var Result=[]; //用於保存徹底匹配的結果集

2.工具函數

function firstMatch(property,ruleSet){
    //將元素的id/class/tag/feature與idRules/classRules/tagRules/featureRules匹配
    //將匹配到的規則以數組返回,未匹配到返回空.
}

function getS(rule){
    //獲取單條rule規則的選擇器數組
}

function Match(s,e){
    //將選擇器s與元素e匹配
    //若是成功匹配則返回true,不然返回false
}

function leftMatch(rule,e){ //第一次匹配事後,用於處理結果集中單條規則的匹配的函數(調用Match函數)
    var s = getS(rule); //獲取單條規則的選擇器數組

    for(var i=s.length-1;i>=0;i--){ //從右到左依次將選擇器與元素e匹配
        
        var flag = Match(s[i],e);
        if(!flag){ //一旦某次匹配失敗,函數直接返回
            return;
        }
    }
    push(Result,rule); //僅當全部選擇器都匹配成功,即徹底匹配時,纔將該條rule添加到Result
}

function ruleMatch(firstmatch,e){ //第一次匹配後的處理函數(調用leftMatch函數)
    if(firstmatch爲空){ //第一次匹配爲空,直接返回
        return;
    }

    for(var i=0;i<=firstMatch.length-1;i++){

        leftMatch(firstMatch[i],e); //第一次匹配不爲空,對每條匹配到的規則調用leftMatch

    }
}

3.id,class,tag,feature匹配的函數及調用順序

//匹配idRules的函數
function idMatch(idRules,e){
    var firstmatch = firstMatch(e.id,idRules);
    ruleMatch(firstmatch,e);
}

//匹配classRules的函數
function classMatch(classRules,e){
    var firstmatch = firstMatch(e.class,classRules);
    ruleMatch(firstmatch,e);
}

//匹配tagRules的函數
function tagMatch(tagRules,e){
    var firstmatch = firstMatch(e.tag,tagRules);
    ruleMatch(firstmatch,e);
}


//匹配featureRules的函數
function featureMatch(featureRules,e){
    var firstmatch = firstMatch(e.feature,featureRules);
    ruleMatch(firstmatch,e);
}

//函數調用順序
idMatch(idRules,e);
classMatch(classRules,e);
tagMatch(tagRules,e);
featureMatch(featureRules,e);

4.說明

  • firstMatch(property,ruleSet)函數: 表明按關鍵選擇器的四種分類進行的第一次匹配,並返回匹配到的結果集(規則的數組)
  • Match(s,e)函數: 表明第一次分類匹配後,對結果集內的單條規則中的單個選擇器與元素的匹配
  • leftMatch(rule,e)函數: 表明第一次分類匹配後,對結果集內的單條規則的全部選擇器(從右至左)與元素的匹配
  • ruleMatch(firstmatch,e)函數 : 表明第一次分類匹配後,完成接下來全部的匹配任務.
  • 元素的內聯樣式是不通過上述匹配過程的,由於它自己就是專爲該元素定義的樣式.

權重(優先級)計算

1.選擇器字符串的權重(specifishity)

(1)css中的權重模型(I-S-A-B-C)

其中:

I: 指代該規則是否爲!important規則

S: 指代該規則是否爲內聯樣式規則

A-B-C: 爲內核定義的三類簡單選擇器

  • 基礎部分: A-B-C模型(以webkit爲例)

    瀏覽器內核用一個32位二進制數來表示某css規則的權重值,其基礎部分的A-B-C模型是指權重計算的三類增量.

    • A: id選擇器,增量爲0x10000
    • B: 類選擇器,屬性選擇器和僞類選擇器,增量爲0x100
    • C: 元素選擇器和僞元素選擇器,增量爲1

    說明:

    • 上述三類增量的定義位於webkit內核中的CSSSelector.h頭文件中
    • 關於I和S,取值只有0和1,不屬於增量模型,咱們能夠假定爲該32位二進制數的最高位和次高位.
    • **通用選擇器(*)**,選擇器組合符(+,>,~,空格,||)不影響權重值
    • 取反僞類 :not()
      • 在selector level 3標準(現行標準)中,:not()只接受一個簡單選擇器爲參數,其自己不影響權重,其權重爲其參數的權重
      • 在selector level 4草案中, :not()接受一個選擇器列表爲參數,其自己不影響權重,其權重爲選擇器列表中權重最高的參數的權重.

2.CSS規則權重的計算:

  • 選擇器字符串中:
    • 每出現一個C類簡單選擇器,權重值增1
    • 每出現一個B類簡單選擇器,權重值增0x100
    • 每出現一個A類簡單選擇器,權重值增0x10000
    • 若是是元素的內聯規則,次高位置1(筆者假設)
    • 若是是!important規則,最高位置1(筆者假設)
  • 意義: 高權重值的規則將覆蓋低權重規則中的相同屬性的值(也就是:設置相同屬性的值,高權重有效,地權重失效)

2.涉及到做者樣式,用戶樣式,默認樣式三種樣式來源時的權重規則

  • 做者樣式/網頁做者樣式(author style sheets): 網頁做者規定的樣式(網頁樣式的主要來源)
  • 用戶樣式(user style sheets): 用戶自定義的個性樣式(各大瀏覽器都在自行廢除中)
  • 默認樣式/瀏覽器樣式(user agent style sheet): 用戶代理的默認樣式(通常用於前兩種都未涉及到的元素的樣式)

通常,網頁開發者和大多數用戶考慮到的只有做者樣式表和默認樣式表(例如:用選擇器*去掉默認樣式中的元素margin和padding),而默認樣式表中不含任何內聯樣式和!important樣式.可是若是咱們考慮上用戶樣式,權重規則可能會有一點不和諧的地方:(優先級從大到小)

用戶樣式!important規則 > 做者樣式!important規則 > 內聯樣式 > 做者樣式普通規則 > 用戶樣式普通規則

上面的優先級鏈中不包含內聯!important規則,是由於通常的!important規則的目的就是爲了之外部樣式覆蓋內聯樣式.

可見, 在普通規則和!important規則上, 兩種樣式表的權重狀況是不一樣的.

  • 通常規則: 做者樣式 優先於 用戶樣式
  • !important規則: 用戶樣式 優先於 做者樣式

可能正是這樣的不和諧,才使得各大瀏覽器都在逐漸廢除用戶樣式吧.可是用戶樣式的實現能夠藉助瀏覽器插件間接實現,不過那又是另一個故事了.


權重相同時的覆蓋原則

前面說到: 相同屬性設置時,高權重覆蓋低權重.那麼若是兩條規則的權重相同時如何決定哪一個有效哪一個無效呢?

首先,這裏的權重相同有兩層含義:

  • 基礎的基於A-B-C模型的權重計算結果相同
  • 在是否爲內聯樣式或!important規則上的表現也相同

那麼,權重相同的狀況有下面幾種:

1.同一外部樣式表中的同權重規則

2.不一樣樣式表中的同權重規則(兩部分)

  • 兩樣式表A和B均是經過link或style引入
  • 樣式表A經過@import規則導入樣式表B
  • 樣式表A,B均是經過link或style引入,但樣式表C是B經過@import導入

3.元素class屬性爲多個值,且各個值同權重時

css2.1規範指出: 若是兩個聲明的權重,來源都相同,後指定的生效. 經過@import引入的樣式表中的規則被認爲位於父樣式表自身的全部規則以前.

而咱們討論同權重規則的覆蓋,就是對這個後指定的理解與解讀:

  • 對於1狀況,後指定意爲靠後的(後面的覆蓋前面的)
  • 對於2狀況
    • A,B均是link/style引入: 後指定,意思是導入樣式表的link/style標籤在html文檔中相對靠後的位置
    • A經過@import規則導入B: 相對於B,A爲後指定
    • A,B是link/style引入,B經過@import導入C
      • 在html文檔中,A的link比B的link靠前: 同權重規則優先級爲B>C>A
      • 在html文檔中,B的link比A的link靠前: 同權重規則優先級爲A>B>C
  • 對於3狀況: 通過測試,各個class值的前後順序不影響覆蓋順序,覆蓋順序的判斷仍按照上面兩個步驟.

下面是一些測試代碼:

/*測試1: 同同樣式表內同權重規則+覆蓋規則與class內值前後順序無關*/
/*html部分:
    <div class="a b"></div>
*/
div{
    width: 100px;
    height: 100px;
    border: 1px solid black;
}

.b {
    background-color: blue;
}

.a {
    background-color: red;
}
/*測試結果: 紅色*/
/*測試2: 不一樣樣式表,相同權重規則*/
/*test1:<link>位於<style>以前*/
/*html部分:
    <link rel="stylesheet" href="csstest2.css">
    <div class="a"></div>
    <style type="text/css">
        .a{
            background-color: red;
        }
    </style>
*/
div{
    width: 100px;
    height: 100px;
    border: 1px solid black;
}
.a {
    background-color: blue;
}
/*測試結果: 紅色*/

/*test2:link位於style以後*/
/*html部分:
    <div class="a"></div>
    <style type="text/css">
        .a{
            background-color: red;
        }
    </style>
    <link rel="stylesheet" href="csstest2.css">
*/
div{
    width: 100px;
    height: 100px;
    border: 1px solid black;
}
.a {
    background-color: blue;
}
/*測試結果: 藍色*/


/*test3: ABC覆蓋規則*/
/*html部分:
    <div class="a"></div>
    <style type="text/css">
        .a{
            background-color: blue;
        }
    </style>
    <link rel="stylesheet" href="csstest2.css">
*/
/*csstest3.css內容
@charset utf-8;
.a {
    background-color:yellow;
}
*/
@import "csstest3.css"; /*必須註釋掉前面全部的測試內容,不然違反了@import的使用規則*/
div{
    width: 100px;
    height: 100px;
    border: 1px solid black;
}
.a {
    background-color: red;
}
/*測試結果:
不做註釋: 紅色
註釋csstest2.css: 黃色
註釋csstest2.css和csstest3.css: 藍色
*/
相關文章
相關標籤/搜索