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

來源:互聯網
上載者:User

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

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

 

在最新的Selector Level 3規範中: 

將三個數字按 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個月前)作者:commit-queue@webkit.org訊息:

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

Patch by Tab Atkins <jackalmage@gmail.com> 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測試的方法來確認問題。在IE6(q、s)中,表現和mozilla一致。在IE7+中,表現和webkit一致。  結論:

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.cpphttp://hg.mozilla.org/mozilla-central/file/17c65d32c7b8/layout/style/StyleRule.cpp#l521瀏覽器的工作原理:新式網路瀏覽器幕後揭秘KB005: CSS 層疊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.