CSS-选择器

常用的选择器

1
2
3
4
5
6
7
8
9
10
# /* ID */
. /* 类名 */
element(div) /* 标签 */
A, E /* 所有A元素和B元素 */
A E /* 元素A的任一后代元素E (后代节点指A的子节点,子节点的子节点,以此类推) */
:first-child /* 第一个子元素 */
:last-child /* 最后一个子元素 */
:nth-child(n) /* 第n个子元素 */
:hover /* 鼠标悬停 */
:active /* 被激活(点击) */

关系形选择器

1
2
3
4
5
6
A, E /* 所有A元素和B元素 */
A E /* 元素A的任一后代元素E (后代节点指A的子节点,子节点的子节点,以此类推) */
A > E /*元素A的任一子元素E(也就是直系后代)*/
E:first-child /*任一是其父母结点的第一个子节点的元素E*/
A + E /*选择紧接在元素A之后的所有 兄弟元素E*/
A ~ E /*同一个父元素下,并且前面有A元素的E*/

A ~ E举例:

1
2
3
4
5
6
7
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
1
2
3
4
/* 为li中除了第一个li以为的li加border */
ul li ~ li {
border: 1px solid #000;
}

伪类选择器

1
2
3
4
5
6
7
8
9
:link /* 选择所有未被访问的链接。 */
:visited /* 选择所有已被访问的链接。 */
:active /* 选择被激活元素。 */
:hover /* 选择鼠标指针位于其上的链接。 */
:focus /* 选择获得焦点的元素。 */
:first-child /* 第一个子元素 */
:nth-child(n) /* p:nth-child(2) 选择属于其父元素的第二个子元素的每个 <p> 元素。 */
:nth-last-child(n) /* p:nth-last-child(2) 择属于其父元素的倒数第二个子元素的每个 <p> 元素。 */
:nth-of-type(n) /* p:nth-of-type(n) 同一个父元素下同类型元素的第n个兄弟 <p> 元素 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<style>
.item:nth-of-type(3) {
color: red;
}
</style>
<section>
<div class="typeWrapper">
<div>第1个DIV</div>
<div>第2个DIV</div>
<div class="item">第3个DIV</div>
<p>第1个P</p>
<p>第2个P</p>
<p class="item">第3个P</p>
</div>
<div class="typeWrapper">
<div>第一个DIV</div>
<p>第一个P</p>
<div class="item">第二个DIV</div>
<p>第二个P</p>
<div>第三个DIV</div>
<p class="item">第三个P</p>
</div>
<div class="item">第三个typeWrapper</div>
</section>
1
2
3
4
5
6
7
:first-of-type /* p:first-of-type 选择属于其父元素的首个 <p> 元素的每个 <p> 元素 */
:only-child /* p:only-child 选择属于其父元素的唯一子元素的每个 <p> 元素。*/
:last-of-type /* p:last-of-type 选择属于其父元素的最后 <p> 元素的每个 <p> 元素。 */
:empty /* p:empty 选择没有子元素的每个 <p> 元素。 */
:checked /* input:checked 选择每个被选中的 <input> 元素。 */
:enabled /* input:enabled 选择每个启用的 <input> 元素。 */
:disabled /* input:disabled 选择每个禁用的 <input> 元素 */

属性选择器

1
2
3
4
5
6
7
[attribute] /* [input] 选择带有 target 属性所有元素。*/
[attribute=value] /* [input=button] 选择 target="_blank" 的所有元素。*/
[attribute~=value] /* [title~=flower] 选择 title 属性包含单词 "flower" 的所有元素。*/
[attribute|=value] /* [lang|=en] 选择 所有lang 属性值以 "en" 开头的所有元素。*/
[attribute^=value] /* a[src^="https"] 选择其 src 属性值以 "https" 开头的每个 <a> 元素。*/
[attribute$=value] /* a[src$=".pdf"] 选择其 src 属性以 ".pdf" 结尾的所有 <a> 元素。*/
[attribute*=value] /* a[src*="abc"] 选择其 src 属性中包含 "abc" 子串的每个 <a> 元素。*/

e.g.覆盖行内样式

1
2
3
<div style="color: red">
You can't change me!
</div>
1
2
3
div[style="color:red"] {
color: green!important;
}

优先级

1
2
3
4
5
!important
内联样式
# /* ID */
. /* 类 */, [type="radio"] /* 属性 */, :hover /* 伪类 */
h1 /* 标签 */, ::before /* 伪元素 */

一种说法是内联样式的权重视为1000,ID权重视为100,类为10,标签为1,关系型为0,对吗?
验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="expectClass">
<div class="index_1">
<div class="index_2">
<div class="index_3">
<div class="index_4">
<div class="index_5">
<div class="index_6">
<div class="index_7">
<div class="index_8">
<div class="index_9">
<div class="index_10">
<div class="index_11">
<div class="index_12" id="selector_id">测试选择器的权重</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
1
2
3
4
5
6
.expectClass .index_1 .index_2 .index_3 .index_4 .index_5 .index_6 .index_7 .index_8 .index_9 .index_10 .index_11 .index_12{
color: burlywood; /* 米黄色 */
}
#selector_id{
color: blueviolet; /* 紫色 */
}

上面的第一种选择法的权重加起来是130,而第二种只有100,结果却是紫色。

jestDemoSuccess

说明那种说法有问题,实际计算法则如下:

  1. 计算选择器中ID选择器的数量(= A)
  2. 计算选择器中的类选择器,属性选择器和伪类的数量(= B)
  3. 计算选择器中类型选择器和伪元素的数量(= C)
  4. 忽略通用选择器
  5. 通配选择符(universal selector)(*), 关系选择符(combinators) (+, >, ~, ‘ ‘) 和 否定伪类(negation pseudo-class)(:not()) 对优先级没有影响。(但是,在 :not() 内部声明的选择器是会影响优先级)
  6. A > B > C, A权重为标签数 * 65536,B权重为标签数 * 256,C权重为标签数 * 1
  7. 当 A 、B 、C 全部相等时,后面声明的值将会是最终的计算值。
  8. 各自的数目最大为255

即首先计算ID的数量,然后计算类选择器属性选择器伪类的数量,接着计算类型选择器(标签)伪元素的数量

e.g.

1
2
3
4
5
6
7
8
9
10
11
*               /* 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 */
div + *[type=text] /* a=0 b=1 c=1 */
div ol li.red /* a=0 b=1 c=3 */
li.red.level /* a=0 b=2 c=1 */
#id /* a=1 b=0 c=0 */
#id:not(div) /* a=1 b=0 c=1 */
.foo :is(.bar, #baz)
/* a=1 b=1 c=0 */

先比较A, A相等再比较B,然后再比较C。全部相等则算最后的。后一级不可能超过前一级,来看下webkit对于选择器权重的解读,一个ID选择器权重最小为65536,而第二级类名选择器最大才65280,标签最大255,所以后一级不可能超过前一级。因此没有!important和内联样式时,ID最大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 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;
}

demo地址:https://github.com/wz71014q/WebWorkSpace/tree/master/projects/learnCSS/selector

参考文档

https://juejin.im/post/5abc4fd7f265da237b2228ee
https://www.w3.org/TR/selectors/#specificity-rules