深入理解CSS選擇器優先順序的計算

来源:http://www.cnblogs.com/shouce/archive/2016/04/11/5376949.html
-Advertisement-
Play Games

選擇器的優先順序關係到元素應用哪個樣式。在CSS2.1的規範(http://www.w3.org/TR/2009/CR-CSS2-20090908/cascade.html#specificity)中是這樣描述的: 如果聲明來自於“style”屬性,而不是帶有選擇器的規則,則記為 1,否則記為 0 ( ...


選擇器的優先順序關係到元素應用哪個樣式。在CSS2.1的規範(http://www.w3.org/TR/2009/CR-CSS2-20090908/cascade.html#specificity)中是這樣描述的:

  1. 如果聲明來自於“style”屬性,而不是帶有選擇器的規則,則記為 1,否則記為 0 (= a)(HTML元素的style屬性也是樣式規則,因為這些樣式規則沒有選擇器,因此記為a=1,b=0,c=0,d=0)
  2. 計算選擇器中 ID 屬性的個數 (= b)
  3. 計算選擇器中其他屬性(類、屬性選擇器)和偽類的個數 (= c)
  4. 計算選擇器中元素名稱和偽元素的個數 (= d)

將四個數字按 a-b-c-d 這樣連接起來(位於大數進位的數字系統中),構成選擇器的優先順序。

 

在最新的Selector Level 3規範中: 

  1. 計算選擇器中 ID 屬性的個數 (= a)
  2. 計算選擇器中其他屬性(類、屬性選擇器)和偽類的個數 (= b)
  3. 計算選擇器中元素名稱和偽元素的個數 (= c)
  4. 忽略通用選擇器*
將三個數字按 a-b-c這樣連接起來(位於大數進位的數字系統中),構成選擇器的優先順序。style屬性計算參考css2.1規範。  

問題:

1、選擇器的整體優先順序如何計算,是像網上說的a*1000+b*100+c*10+d嗎?

       答:不是。這種回答明顯是望文生義。四級(a、b、c、d)之間並不是簡單的相加關係。同一級(例如:a對a)的才具有可比關係。   分析: 以下為webkit的webCore中關於優先順序計算的代碼(http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSSelector.cpp 複製代碼
unsigned CSSSelector::specificity() const
{
    // make sure the result doesn't overflow
    static const unsigned maxValueMask = 0xffffff; // 整個選擇器的最大值,十進位表示:idMask + classMask + elementMak = 16777215
    static const unsigned idMask = 0xff0000; // ID選擇器的最大值,十進位表示:(16*16+16)*16^4=16711680
    static const unsigned classMask = 0xff00; // class(偽類、類)選擇器的最大值,十進位表示:(16*16+16)*16^2=65280
    static const unsigned elementMask = 0xff; // 元素選擇器的最大值,十進位表示:16*16+16=255

    if (isForPage())
        return specificityForPage() & maxValueMask;

    unsigned total = 0;
    unsigned temp = 0;

    for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) {
        temp = total + selector->specificityForOneSelector();
        // Clamp each component to its max in the case of overflow.
        if ((temp & idMask) < (total & idMask)) // 判斷是否為ID選擇器
            total |= idMask; // 保證ID選擇器的同類疊加不會超過ID選擇器的總最大值,下同
        else if ((temp & classMask) < (total & classMask))
            total |= classMask;
        else if ((temp & elementMask) < (total & elementMask))
            total |= elementMask;
        else
            total = temp;
    }
    return total;
}

inline unsigned CSSSelector::specificityForOneSelector() const
{
    // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
    // isn't quite correct.
    switch (m_match) {
    case Id:
        return 0x10000; // ID選擇器權重

    case PseudoClass:
        // FIXME: PsuedoAny should base the specificity on the sub-selectors.
        // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html
        if (pseudoClassType() == PseudoClassNot && selectorList())
            return selectorList()->first()->specificityForOneSelector();
        FALLTHROUGH;
    case Exact:
    case Class:
    case Set:
    case List:
    case Hyphen:
    case PseudoElement:
    case Contain:
    case Begin:
    case End:
        return 0x100; // class選擇器權重

    case Tag:
        return (tagQName().localName() != starAtom) ? 1 : 0; // 元素選擇器權重
    case Unknown:
        return 0;
    }
    ASSERT_NOT_REACHED();
    return 0;
}
複製代碼         從上面的代碼可以看出,在webkit中,對於a級選擇器(“style”屬性的樣式規則),根本不參與優先順序運算的過程。對於b級(ID選擇器)、c級(class選擇器)、d級(元素選擇器),每一級都有自己的最大值(最大數目255),超出時就會應用其最大值(最大數目)。b級最大值為0xff0000(16711680),權重為0x1000(65536),數目超過256時仍然使用最大值。c級、d級相似。所以並不存在低一級超出一定數目後導致高一級進一齣現覆蓋的情況。在一個選擇器組(em:#a .d div)中,所有選擇器的加和不會超過16777215(每一類的選擇器都保證了不會超出最大值的情況)。demo:http://jsbin.com/duker/2。對於!important,webkit是走的另一條路徑(具有!important的樣式規則大於沒有!important的樣式規則,只有在同時具有!important屬性時才會比較選擇器的整體優先順序)。整體來說,在webkit中,!important>inline style>ID>class>tag。       webkit是在http://trac.webkit.org/changeset/130444/trunk/Source/WebCore/css/CSSSelector.cpp這一次的修訂中加上對於優先順序溢出的處理的(chrome發佈版本很快,今年改用了blink,可以認為chrome都遵守了特殊性(優先順序)計算的標準):
時間戳:2012-10-04 19:04:44 (20個月前)作者:[email protected]消息:

選擇器特殊性類別溢出到高類別
https://bugs.webkit.org/show_bug.cgi?id=98295

Patch by Tab Atkins <[email protected]> on 2012-10-04
Reviewed by Eric Seidel.

這一次添加的補丁是為了對於CSS選擇器的特殊性添加溢出策略。

以前我們並不會檢測每個類別的特殊性溢出問題。原始的策略是:把每個類別存儲為一個位元組(2^8=256),然後整體存在一個無符號整型數中。這樣的話就會導致256個同一類別的單選擇器等於1個高類別的選擇器。但是這違反了選擇器的特殊性規則,導致樣式規則排序問題。

Tests: /fast/selectors/specificity-overflow.html

  • css/CSSSelector.cpp:

(WebCore::CSSSelector::specificity):

          mozilla中關於優先順序計算的代碼(地址為http://hg.mozilla.org/mozilla-central/file/7297dedf2416/layout/style/StyleRule.cpp(472行-537行)): 複製代碼
int32_t nsCSSSelector::CalcWeightWithoutNegations() const
 {
   int32_t weight = 0;
 
 #ifdef MOZ_XUL
   MOZ_ASSERT(!(IsPseudoElement() &&
                PseudoType() != nsCSSPseudoElements::ePseudo_XULTree &&
                mClassList),
              "If non-XUL-tree pseudo-elements can have class selectors "
              "after them, specificity calculation must be updated");
 #else
   MOZ_ASSERT(!(IsPseudoElement() && mClassList),
              "If pseudo-elements can have class selectors "
              "after them, specificity calculation must be updated");
 #endif
   MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
              "If pseudo-elements can have id or attribute selectors "
              "after them, specificity calculation must be updated");
 
   if (nullptr != mCasedTag) {
     weight += 0x000001;
   }
   nsAtomList* list = mIDList;
   while (nullptr != list) {
     weight += 0x010000;
     list = list->mNext;
   }
   list = mClassList;
 #ifdef MOZ_XUL
   // XUL tree pseudo-elements abuse mClassList to store some private
   // data; ignore that.
   if (PseudoType() == nsCSSPseudoElements::ePseudo_XULTree) {
     list = nullptr;
   }
 #endif
   while (nullptr != list) {
     weight += 0x000100;
     list = list->mNext;
   }
   // FIXME (bug 561154):  This is incorrect for :-moz-any(), which isn't
   // really a pseudo-class.  In order to handle :-moz-any() correctly,
   // we need to compute specificity after we match, based on which
   // option we matched with (and thus also need to try the
   // highest-specificity options first).
   nsPseudoClassList *plist = mPseudoClassList;
   while (nullptr != plist) {
     weight += 0x000100;
     plist = plist->mNext;
   }
   nsAttrSelector* attr = mAttrList;
   while (nullptr != attr) {
     weight += 0x000100;
     attr = attr->mNext;
   }
   return weight;
 }
 
 int32_t nsCSSSelector::CalcWeight() const
 {
   // Loop over this selector and all its negations.
   int32_t weight = 0;
   for (const nsCSSSelector *n = this; n; n = n->mNegations) {
     weight += n->CalcWeightWithoutNegations();
   }
   return weight;
 }
複製代碼       和webkit一樣,inline style元素不計入計算。對於b級(ID)、c級(class)、d級(tag)的最大值和最小值也都和webkit保持一致。不同的是在mozilla中並沒有對於同一類別的選擇器進行最大值控制,而是結果直接相加。這樣的話就會導致同一級選擇器數目多於255時高一級進一,也就是結果溢出的問題。而且對於整個選擇器組的優先順序計算因為沒有類似於webkit的按位與運算來保證結果不溢出,只是簡單的相加,在mozilla中就可能出現溢出的問題。         因為IE無法閱讀代碼,所以對於IE系列只能採取demo測試的方法來確認問題。在所有IE系列(q、s)中,表現都和mozilla一致。包括最新的IE11。   註:CSS選擇器還有一個繼承性: 複製代碼

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<style>
*{font-size:40px;}
#test {font-size:12px !important;}
p {font-size:24px;}
</style>
<body>
<div id="test"><p>test text</p></div>
</body>

複製代碼

在所有瀏覽器中文字都會應用p {font-size:24px;}。如果把這句去掉的話,就會應用*{font-size:40px;},*包括p。(繼承的樣式沒有優先順序)

   

結論:

1、優先順序計算時跨級相加應註意溢出問題;

2、優先順序計算不包括inline style和!important;

3、優先順序計算只有同一類別才具有可比性(一般也不會有人定義超出255個的同一選擇器)。

  順便引用stackoverflow上的一個回答來結束這篇文章:

I am currently using the book CSS Mastery: Advanced Web Standards Solutions.

Chapter 1, page 16 says:

To calculate how specific a rule is, each type of selector is assigned a numeric value. The specificity of a rule is then calculated by adding up the value of each of its selectors. Unfortunately, specificity is not calculated in base 10 but a high, unspecified, base number. This is to ensure that a highly specific selector, such as an ID selector, is never overridden by lots of less specific selectors, such as type selectors.

參考文章:

http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSSelector.cpp http://hg.mozilla.org/mozilla-central/file/17c65d32c7b8/layout/style/StyleRule.cpp#l521 瀏覽器的工作原理:新式網路瀏覽器幕後揭秘 KB005: CSS 層疊     分類: CSS 標簽: 選擇器優先順序, webkit 好文要頂 關註我 收藏該文聯繫我 shinnyChen
關註 - 1
粉絲 - 6     +加關註 2 1   (請您對文章做出評價)   « 上一篇:IE中對於stylesheet的個數限制
» 下一篇:[翻譯]localStorage性能的好壞
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Atiti。流量提升軟體設計大綱規劃 v1 q45 1. 通用數據管理1 2. 網頁Url管理模塊1 3. 網站domain管理1 4. ad廣告管理2 5. Task任務管理2 6. 任務執行功能::進入網站,隨機瀏覽網頁2 7. 系統設置2 8. 界面跨平臺h52 9. 開發語言java+h53 ...
  • atitit.userService 用戶系統設計 v5 q330 1. 新特性1 2. Admin login1 3. 用戶註冊登錄2 3.1. <!-- 會員註冊使用 --> 商家註冊2 3.2. <!-- 會員登錄使用 -->3 3.3. <!-- 會員退出登錄 -->3 3.4. <!-- ...
  • 在所有的設計模式開篇中,總是說一個好的架構,或多或少都會有設計模式的出現。當然或多或少也會使用設計模式的相關原則: SOLID+迪米爾原則 1.優化代碼的第一步:單一職責原則 S:單一職責鏈原則:英文名稱為Single Responsibility Principle(SRP) 定義:就一個類而言, ...
  • 寫在最前面:轉載請註明出處 目錄置頂: 關於項目 基於DDD領域驅動設計的WCF+EF+WPF分層框架(1) 架構搭建 基於DDD領域驅動設計的WCF+EF+WPF分層框架(2) WCF服務端具體實現 基於DDD領域驅動設計的WCF+EF+WPF分層框架(3) WCF客戶端配置以及代理 基於DDD領 ...
  • 移動設備已經成為在任何時間的一部分工作。在小型和中型組織人員使用個人平板電腦和智能手機業務。開源社區在這個移動應用工作的增長起到了關鍵作用。有許多開放源代碼的應用程式,可以幫助提高你的創造力。今天我們所列舉的10大開源工具,你會在工作中找到有用的。 1.Convertigo 這是用於開發和部署應用程 ...
  • PC的早期階段,也是傳統的C/S模式居多,後進化到B/S模式,並產生了SaaS、雲計算等概念和應用。從客戶端進化到瀏覽器最大好處是客戶端無需更新,減少了大量的更新成本,只需伺服器端進行更新。這也是為什麼現在流行webQQ, google docs, photoshop網頁版的原因。現在同時很多軟體廠 ...
  • 瀏覽器的事件模型 DOM第0級事件模型 他的屬性提供了關於當前正被處理的已觸發事件的大量信息。這包括一些細節,比如在哪個元素上觸發的事件、滑鼠事件的坐標以及鍵盤事件中單擊了哪個鍵。 當觸發 dom 樹中一個元素上的事件時,事件模型會檢查這個元素是否已經創建了特定的事件處理器。如果是,就會調用已創建的 ...
  • 序言 在今天,JavaScript已經成為了網頁編輯的核心。尤其是過去的幾年,互聯網見證了在SPA開發、圖形處理、交互等方面大量JS庫的出現。 如果初次打交道,很多人會覺得js很簡單。確實,對於很多有經驗的工程師,或者甚至是初學者而言,實現基本的js功能幾乎毫無障礙。但是JS的真實功能卻比很多人想象 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...