深入理解CSS选择器优先级的计算
选择器的优先级关系到元素应用哪个样式。在CSS2.1的规范中是这样描述的:
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中关于优先级计算的代码
从上面的代码可以看出,在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 ... 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中关于优先级计算的代码
因为IE无法阅读代码,所以对于IE系列只能采取demo测试的方法来确认问题。在IE6(q、s)中,表现和mozilla一致。在IE7+中,表现和webkit一致。
结论:
1、优先级计算时跨级相加应注意溢出问题;
2、优先级计算不包括inline style和!important;
3、优先级计算只有同一类别才具有可比性(一般也不会有人定义超出255个的同一选择器)。
顺便引用stackoverflow上的一个回答来结束这篇文章:
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中关于优先级计算的代码
- 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 ... 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中关于优先级计算的代码
- 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;
- }
因为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.
【深入理解CSS选择器优先级的计算】相关文章
2. 征服高级CSS选择器
3. 编写高效的CSS选择器
本文来源:https://www.51html5.com/a1057.html
上一篇:WEB前端使用的CSS3选择器
下一篇:8款超酷而实用的CSS3按钮动画
﹝深入理解CSS选择器优先级的计算﹞相关内容
- CSS3每日一练之选择器-状态伪类选择器[三]
- CSS3每日一练之选择器-状态伪类选择器[二]
- CSS3每日一练之选择器-状态伪类选择器[一]
- CSS3每日一练之选择器-结构性伪类选择器[六]
- CSS3每日一练之选择器-结构性伪类选择器[五]
- CSS3每日一练之选择器-结构性伪类选择器[四]
- CSS和CSS3按钮选择器对比
- 详解CSS的滑动门技术
- 《图解css3:核心技术与案例实战》
- 诺基亚将推平板电脑,Windows优先,Android候选