選擇器的優先順序關係到元素應用哪個樣式。在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)中是這樣描述的:
- 如果聲明來自於“style”屬性,而不是帶有選擇器的規則,則記為 1,否則記為 0 (= a)(HTML元素的style屬性也是樣式規則,因為這些樣式規則沒有選擇器,因此記為a=1,b=0,c=0,d=0)
- 計算選擇器中 ID 屬性的個數 (= b)
- 計算選擇器中其他屬性(類、屬性選擇器)和偽類的個數 (= c)
- 計算選擇器中元素名稱和偽元素的個數 (= d)
將四個數字按 a-b-c-d 這樣連接起來(位於大數進位的數字系統中),構成選擇器的優先順序。
在最新的Selector Level 3規範中:
- 計算選擇器中 ID 屬性的個數 (= a)
- 計算選擇器中其他屬性(類、屬性選擇器)和偽類的個數 (= b)
- 計算選擇器中元素名稱和偽元素的個數 (= c)
- 忽略通用選擇器*
問題:
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]消息:mozilla中關於優先順序計算的代碼(地址為http://hg.mozilla.org/mozilla-central/file/7297dedf2416/layout/style/StyleRule.cpp(472行-537行)):選擇器特殊性類別溢出到高類別
https://bugs.webkit.org/show_bug.cgi?id=98295Patch 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):
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性能的好壞