关于选择器优先级的误解[刷新三观,深入理解选择器优先级]

优先级并不是网上说的ABCD四个级别,1000、100、10、1这样,而是根据选择器数目和选择器种类来计算的。

选择器的优先级关系到元素应用哪个样式。在CSS2.1的规范中是这样描述的:
1.如果声明来自于“style”属性,而不是带有选择器的规则,则记为 1,否则记为 0 (= a)(HTML元素的style属性也是样式规则,因为这些样式规则没有选择器,因此记为a=1,b=0,c=0,d=0)
2.计算选择器中 ID 属性的个数 (= b)
3.计算选择器中其他属性(类、属性选择器)和伪类的个数 (= c)
4.计算选择器中元素名称(标签名)和伪元素(:after :before)的个数 (= d)

 

将四个数字按 a-b-c-d 这样连接起来(位于大数进制的数字系统中),构成选择器的优先级。

 

在最新的Selector Level 3规范中: 

1.计算选择器中 ID 属性的个数 (= a)
2.计算选择器中其他属性(类、属性选择器)和伪类的个数 (= b)
3.计算选择器中元素名称和伪元素的个数 (= c)
4.忽略通用选择器*


将三个数字按 a-b-c这样连接起来(位于大数进制的数字系统中),构成选择器的优先级。style属性计算参考css2.1规范。

 (子选择器同样包含在优先级中,比如说body空格p,这样选中的元素算是两个标签的优先级)

问题:


1、选择器的整体优先级如何计算,是像网上说的a*1000+b*100+c*10+d吗?

       答:不是。这种回答明显是望文生义。四级(a、b、c、d)之间并不是简单的相加关系。同一级(例如:a对a)的才具有可比关系。

2、优先级是否如网上说的,外部css<内部css<style属性?

  答:不是。同上,style确实是优先级最高,但是内部和外部css效果相同,决定哪个生效的仅仅是优先级而已。

分析:

以下为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都遵守了特殊性(优先级)计算的标准)

 

 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;
 }

 

和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个的同一选择器)。

 

转自:http://www.html5cn.org/article-6407-1.html

 

 

通常我们可以将CSS的优先级由高到低分为六组:

  1. 无条件优先的属性只需要在属性后面使用 !important 。它会覆盖页面内任何位置定义的元素样式。当然,IE 6不支持这个属性,于是它也成为一种hack被很多人所熟知,真正使命被人淡忘。
  2. 第二高位的优先属性是在html中给元素标签加style。由于该方法会造成CSS的难以管理,所以不推荐使用。
  3. 第三级优先的属性是由一个或多个id选择器来定义的。例如 #id{margin:0;} 会覆盖 .classname{margin:3px;}。
  4. 第四级的属性由一个或多个 类选择器、属性选择器、伪类选择器定义。如 .classname{margin:3px;} 会覆盖 div{margin:6px;}
  5. 第五级由一个或多个 类型选择器定义。如 div{margin:6px;}  覆盖  *{margin:10px;} 。
  6. 最低一级的选择器是为一个通配选择器,用*{margin:10px;}这样的类型来定义。

另外,可以用一句极为简单的话来描述CSS优先级问题:当一个CSS选择器拥有更多的高级别属性时,它的优先级就会比较高。如 #i100 *.c20 *.c10{} 的优先级就会高于 #i100 *.c10 div p span em{} ,这是由于前者拥有一个第三级和两个第四级的选择器而后者第三级和第四级的选择器各为一个,当然前者优先级会比较高。

CSS选择器是一个非常灵活的CSS属性,优雅的使用它会使你的页面代码不再臃肿,而且还可以作为hack的妙用方法之一。

当比较多个相同级别的CSS选择器优先级时,它们定义的位置将决定一切。下面从位置上将CSS优先级由高到低分为六级:

  1. 位于<head/>标签里的<style/>中所定义的CSS拥有最高级的优先权。
  2. 第二级的优先属性由位于 <style/>标签中的 @import 引入样式表所定义。
  3. 第三级的优先属性由<link/>标签所引入的样式表定义。
  4. 第四级的优先属性由<link/>标签所引入的样式表内的 @import 导入样式表定义。
  5. 第五级优先的样式有用户设定。
  6. 最低级的优先权由浏览器默认。

理解了这些CSS优先级问题后,也就不必苦恼于样式定于中的此类问题了。

posted @ 2015-05-14 23:42  光闪  阅读(504)  评论(0编辑  收藏  举报