关于CSS选择器的权重计算方法纠正

什么是选择器?

选择器表示结构。该结构可以用作条件(例如,在CSS规则中),其确定选择器在文档树中匹配哪些元素,或者作为对应于该结构的HTML或XML片段的平面描述。

计算选择器的特异性(特殊性)

提到css权重计算,初学者可能知道按照选择器分类,赋予权重值和权重系数等等。但是最终计算时,不深入了解就会想当然的觉得权重相乘相加,最终谁最大听谁的,这是不对的。

听到最多的例子如下:

a-b-c

  • ID: 1000(a
  • Class,属性选择器:100(b
  • Element,伪类选择器:10(c

!important > inline style > ID > class > tag

那么对于这样的一个例子

    <style>
        /*a*/
        #id .p span {
            color: red;
        }
        /*b*/
        #id span {
            color: blue;
        }
    </style>

    <div id="id">
        <p class="p">
            <span>SPAN</span>
        </p>
    </div>

a选择器的总权重值为:(1000 * 1) + (100 * 1) + (10 * 1) = 1110
b选择器的总权重值为:(1000 * 1) + (100 * 1) + (10 * 0) = 1100

那么将会使用a选择器的样式应用到DOM树中,最终被渲染出来。

exactly,但是如果真是这样计算权重,只使用b类型选择器,通过改变数量,那么一样可以使得总权重大于a选择器啊。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #demo span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span span span span span span span span span span span span span span span span span span span
        span span span span span span {
        display: block;
        height: 200px;
        height: 100px;
        background-color: red;
        }
        
        #demo .test {display: block; height:200px;height: 100px; background-color: black;}

    </style>
</head>

<body>
    <div id="demo"></div>

    <script>
        var a = document.getElementById("demo")
        b = {};
        for (var i = 0; i < 220; i++) {
            b = document.createElement("span");
            if (i == 219) b.className = "test";
            a.appendChild(b);
            a = b;
        }
    </script>

</body>
</html>

先定义好css

  • 一种就是上例中a构造类型的选择器,由idclass构成
  • 另一种则是大量element名和id构成的选择器

而JS部分,暴力span生span,最终会嵌套220个span,这样一来,1000 + (220 * 10)选择器怎么也比1000 + 100的权重大了吧?最终应该呈现红色背景色才对。

可是......

QQ截图20181217030359.jpg

啪啪啪!脸疼不?

说好的1000 + (220*10)> 1000 + 100呢?

为什么会这样?因为根本就不是那样计算的,或者说,权重值不对。

如何正确计算权重值

W3C文档:Calculating a selector’s specificity

关于数量

选择器的特异性是针对给定元素计算的,如下所示:

  • 计算选择器中ID选择器的数量(= A)
  • 计算选择器中的类选择器,属性选择器和伪类的数量 (= B)
  • 计算选择器中类型选择器和伪元素的数量(= C)
  • 忽略通用选择器

注意,比较的是数量。
例子:#id .p span {}这样的选择器,a-b-c为1-1-1。

通过比较三个组分来比较特异性:具有较大A值的特异性更具体; 如果两个A值相关,则具有较大B值的特异性更具体; 如果两个B值也相关联,则具有较大C值的特异性更具体; 如果所有值都绑定,则两个特征相等。

用boolean表示就是a || b || c

文档也给出一个示例:

Examples:
*               /* a=0 b=0 c=0 */
LI              /* a=0 b=0 c=1 */
UL LI           /* a=0 b=0 c=2 */
UL OL+LI        /* a=0 b=0 c=3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 */
UL OL LI.red    /* a=0 b=1 c=3 */
LI.red.level    /* a=0 b=2 c=1 */
#x34y           /* a=1 b=0 c=0 */
#s12:not(FOO)   /* a=1 b=0 c=1 */
.foo :is(.bar, #baz)
                /* a=1 b=1 c=0 */

但是仅靠数量还不行。


关于权重(重点)

深入理解CSS选择器优先级的计算这篇文章中,作者找到了webkit中的详细解析代码。

其中一段:

    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

// ....................

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;
}
  • 对于b级(ID选择器)、c级(class选择器)、d级(元素选择器),每一级都有自己的最大值(最大数目255)超出时就会应用其最大值(最大数目)
  • b级最大值为0xff0000(16711680),权重为0x1000(65536),数目超过256时仍然使用最大值。
  • c级、d级相似。所以并不存在低一级超出一定数目后导致高一级进一出现覆盖的情况。

一个选择器如果只有100个Id,那么b级权重就是:100 * 65536 = 6553600
如果有255个,255 * 65536 = 16711680超过255个仍然使用最高权重16711680表示。

c级权重(Cmax < Bmax)最大值远低于b级,这就避免了低位数量多进位覆盖高位权重情况

over~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,891评论 0 13
  • 选择题部分 1.(),只有在发生短路事故时或者在负荷电流较大时,变流器中才会有足够的二次电流作为继电保护跳闸之用。...
    skystarwuwei阅读 12,246评论 0 7
  • 数据结构与算法 1.算法的有穷性是指( )。答案:A A)算法程序的运行时间是有限的 B)算法程序所处理的数据量是...
    织梦学生阅读 3,225评论 1 15
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,789评论 2 89
  • 问题描述 编程语言书籍中经常解释值类型被创建在栈上,引用类型被创建在堆上,但是并没有本质上解释这堆和栈是什么。我仅...
    大爷进城务工阅读 298评论 1 3