CSS: Concept: Cascade: Specificity

20th August 2020 at 2:19pm
CSS: Concept: Cascade

背景

Specificity 是用来解决这样一个问题:对于有着多个互相冲突的 CSS declaration 的元素,应该选择并应用哪个 declaration 的规则。比如下面的例子:

<h1 class="title" id="page-title">Hello World</h1>
h1 {
  font-family: sans-serif;
}
.title {
  font-family: serif;
}
#page-title {
  font-family: monospace;
}

此时应该应用哪个 font-family

规则

Specificity

CSS 中有这三类 selector,它们的 specificity 依次增加:

  1. Type selectors (h1) and pseudo-elements (::before)
  2. Class selectors (.title), attributes selectors ([type="radio"]) and pseudo-classes (:hover)
  3. ID selectors (#page-title)

CSS 会优先选择 specificity 更高的。

具体的计算规则在 CSS 规范 中,或者参考这个形象生动的 CSS SpeciFISHity

Inline and !important

内联样式(style="font-family: sans")优先级高于外部样式。但是 !important 加入时会影响。具体优先级从高到低:

  1. inline !important
  2. non-inline !important
  3. inline normal
  4. non-inline normal

Exceptions

规则中还有一些例外(:is(), :not, :where() 等),比较罕见,不在这里做描述。需要时查询相应文档。

Directly targeted element vs. inherited

考虑这样的 CSS 及 HTML:

#parent {
  color: green;
}

h1 {
  color: purple;
}
<body id="parent">
  <h1 class="a">Here is a title!</h1>
</body>

渲染出来的会是 purple。因为 h1 选择器直接选中了 <h1>,优先级会高于继承的属性。把 h1 换成 .a 也是一样的结果。

Special Order in styling <a> with pseudo-classes

写链接的 CSS 样式时,经常会用到这些 pseudo-classes::link, :visited, :hover:active。实际写样式时,它们的 specificity 都是一致的,因此需要按这样的顺序写:

a:link {
  color: blue;
  text-decoration: none;
}
a:visited {
  color: purple;
}
a:hover {
  color: yellow;
  text-decoration: underline;
}
a:active {
  color: red;
}

如果你把 a:hovera:active 交换位置,那么鼠标点下(:active)链接时,颜色不会变成 red,而是维持在 :hover 状态下的 :yello

下面是一个实例的例子:

Best Practice

CSS in Depth: Ch01 给出了一个非常中肯的建议,写 CSS 时尽量保持 specificity 在低的水平:

As you can see from these examples, specificity tends to become a sort of arms race. This is particularly the case with large projects. It is generally best to keep specificity low when you can, so when you need to override something, your options are open.

同时书中还总结了两个建议:

  • 不要在 selector 中用 ID
  • 不要用 !important

对于 JS 库类作者,书中强烈建议不要用 JS 给元素加内联样式。这会导致使用方必须在 CSS 用大量的 !important 来覆盖你的样式。

Resources