选择器与样式优先级


# 选择器与样式优先级

# 选择器写法

# 一般选择符

  • 类型选择符(通过元素名指定)
  • ID 选择符(由井号 # 开头)
  • 类选择符(由句点 . 开头)

可以将类型选择符和类选择符结合使用,以指定特定的 HTML 元素下特定 class 的样式。

实例:

p.date-postd {
  color: #ccc;
}
1
2
3

# 组合选择符

  • 后代选择符(以空格分隔)
  • 子选择符(以大于号分隔)
  • 相邻同辈选择符(以加号分隔)
  • 一般同辈选择符(以波浪号分隔)

# 后代选择符

后代选择符的写法是在两个选择符之间添加空格,用于选取某个或某组元素的所有后代元素。

实例:

/* 只有作为块引用后代的段落元素会被选中,从而缩进,其他段落都不会缩进 */
blockquote p {
  padding-left: 2em;
}
1
2
3
4

# 子选择符

子选择符的写法是在两个选择符之间添加大于号,与后代选择符不同,它只选择一个元素的直接后代,也就是子元素。

实例:

/* 选择了 div 元素中所有直接子元素 p */
div > p {
  background-color: yellow;
}
1
2
3
4

# 相邻同辈选择符

相邻同辈选择符的写法是在两个选择符之间使用加号,它可以选择紧接在某个元素后面,并与该元素拥有共同父元素的元素。

实例:

/* 选择了 div 元素后的第一个 p 元素 */
div + p {
  background-color: yellow;
}
1
2
3
4

这样选择 div 元素后的第一个段落是可行的,但更简单、更容易维护的方式,还是为这一段增加一个类名。

# 一般同辈选择符

一般同辈选择符的写法是在两个选择符之间使用波浪号,它可以选择所有指定元素之后的相邻兄弟元素。

实例:

/* 选择了 div 元素后的所有 p 元素 */
div ~ p {
  background-color: yellow;
}
1
2
3
4

注:相邻同辈选择符和一般同辈选择符都不会选择前面的同辈元素,这是因为浏览器会按照元素在页面中出现的先后次序给它们应用样式。

# 通用选择符

通用选择符可以匹配任何元素,使用星号表示。

实例:

* {
  padding: 0;
  margin:0;
}
1
2
3
4

但这样写可能带来很多意想不到的后果,特别是会影响 button、select 等表单元素。如果想重设样式,最好还是像下面这样明确指定元素:

h1, h2, h3, h4, h5, h6,
ul, ol, li, dl, p {
  padding: 0;
  margin: 0;
}
1
2
3
4
5

# 属性选择符

属性选择符基于元素是否有某个属性或者属性是否有某个值来选择元素。

# 根据是否有某个属性

实例:
当鼠标指针悬停在某个带有 title 属性的元素上时,多数浏览器都会显示一个提示条。利用这种行为,可以借助 <abbr> 元素对某些缩写词给出详尽的解释:

<p>I'm reading a book called you don't know <abbr title="JavaScript">js</abbr>.</p>
1

可是,如果不把鼠标放在这个元素上,谁也不知道它还会显示缩写词的解释。为此,可以使用属性选择符给带有 title 属性的 abbr 元素添加不同的样式,比如在缩写词下面加一条点划线,然后把悬停状态的鼠标指针改成问好。

abbr[title] {
  border-bottom: 1px dotted #999;
}

abbr[title]:hover {
  cursor: help;
}
1
2
3
4
5
6
7

# 根据属性是否有某个值

除了可以根据是否存在某个属性来选择元素,还可以根据特定的属性值来应用样式。

实例:
下面这个例子可以用来修正一个问题,即鼠标悬停在提交按钮上时,不同浏览器显示的光标不一致。有了这条规则,所有 type 属性值为 submitinput 元素在鼠标指针悬停时,都会显示一个手状光标。

input[type="submit"] {
  cursor: pointer;
}
1
2
3

有时候我们关心的是属性值是否匹配某个模式,而非某个特定值。这时候,通过给属性选择符中的等号前面加上特殊字符,就可以表达出想要匹配的值的形式了。

在属性选择符中常用的特殊字符如下表所示:

选择器 描述
a[href^="http:"] 要匹配以某些字符开头的属性值,在等号前面加上插入符(^)。
img[src$=".jpg"] 要匹配以某些字符结尾的属性值,在等号前面加上美元符号($)。
a[href*="/about/"] 要匹配包含某些字符的属性值,在等号前面加上星号(*)。
a[rel~=next] 要匹配以空格分隔的字符串中属性值(比如 rel 属性的值),在等号前面加上波浪号(~)。
a[lang|=en] 要匹配开头是指定值或者指定值后连着一个短划线的情况,比如 enen-us,在等号前面加上竖线(|)。这种方式不常用。

更多:CSS 选择器参考手册 (opens new window)

# 样式优先级

当声明冲突时,CSS 会根据三种条件来决定样式优先级:

  • 样式表的来源

    行内样式 > 嵌入样式 > 外链样式

  • 选择器优先级

    • 如果选择器的 ID 数量更多,则它会胜出(即它更明确)。
    • 如果 ID 数量一致,那么拥有最多类的选择器胜出。
    • 如果以上两次比较都一致,那么拥有最多标签名的选择器胜出。
  • 源码顺序

    如果两个声明的来源和优先级相同,那么后出现的样式(在样式表中出现较晚,或者位于页面较晚引入的样式表中)会覆盖先出现的样式。
    例如:link(链接)visited(访问)hover(悬停)active(激活),就得严格遵守这个顺序,否则会带来意想不到的结果。

冲突的声明不同的来源或者重要性是不是内联样式(作用域)选择器是否有不同的优先级使用源码顺序里较晚出现的声明使用更高优先级的来源里的声明使用内联声明使用更高先级的声明

(层叠的规则流程图,展示了声明的优先顺序)

# 经验法则

在使用选择器时有一些通用的经验法则,如果遵守这些法则会很有用(特殊情况除外)。

  • 最好让优先级尽可能低
    这样当需要覆盖一些样式时,才能有选择空间。

  • 在选择器中不要使用 ID
    就算只用一个 ID 时也会大幅提升优先级,当需要覆盖这个选择器时,通常找不到另一个更有意义的 ID,于是就会复制原来的选择器,然后加上另一个类。

  • 不要使用 !important
    它比 ID 更难覆盖,一旦引入一个 !important,想要覆盖原先的声明,就会带来更多的 !important,最终会让一切回到起点。

  • 当创建一个用于分发的模块(例如 NPM 包)时,尽量不要使用行内样式
    否则开发人员要么全盘接受包里的样式,要么给每个想修改的属性加上 !important。
    正确的做法是在包里包含一个样式表,这样用户就可以在使用这份样式表的同时,在不引入优先级竞赛的前提下,自定义其中的样式。

# 参考资料

  • 《精通CSS 高级Web标准解决方案(第3版)》

(完)