Skip to content

层叠 / 继承 / 优先级

概念

写 CSS 有点像命令式编程的方式,写好一些规则集,这些规则集合通过所谓的 CSS 选择器组织,然后作用于元素身上。听起来很简单,“样式怎么写的,浏览器就是怎么渲染的”。然后,有些时候,事情可能变得很复杂,尤其是在项目比较庞大的时候,总会出现这样的问题,“为什么布局会变成这样?为什么不生效啊?为什么变成这个鬼样子了?。。。”,能够快速的还原设计稿是一项能力,问题的排查定位与解决是经验与原理的深度结合,拥有项目大局观是成为高级工程师以及“架构师”的必备能力。

默认样式表与用户样式表

我们常说一些元素具有什么默认样式,什么块级元素 / 行内元素的,其实这些东西与 HTML 标签有关系吗?实际上是没有关系,因为某个元素默认具有哪些特征,浏览器在源码中就已经规定好了,这就是所谓的“默认样式表”,贴出源码地址,增强说服力:https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/core/html/resources/html.css,我们描述特征,这是 CSS 赋予的能力,与 HTML 标签没有关系。

所谓的用户样式表,其实就是我们引入的:外部样式文件 / style标签中直接书写样式 / 行内样式。这些都会覆盖默认的样式规则集。

理解优先级

大多时候,造成样式规则不起作用的元凶就是**新的样式规则无法覆盖旧的样式规则。**在任何地方你都以去修改元素的样式,但至于是否生效,就要理解样式作用的优先级规则。

我们逐渐缩小讨论范围:

  • 大前提

    你不可能纯靠浏览器的默认样式写页面的,所有一定会有用户样式表的存在。

    用户样式表 > 默认样式表

  • 用户样式表

    行内样式 > 选择器

  • 选择器

    id选择器 > class选择器 > 元素选择器

    这里注意一下,很多人在八股上看到什么所谓的“权重值”计算,我觉得这是没有什么意义的。当你有这样的意识:写的样式没有生效时,大概率是优先级不够,我觉得就 OK 了。至于那些规则集的优先级比较高,你可以通过浏览器的调试控制台进行查看,找到对应的原因,而不是去看代码瞎猜。这个过程就有点像打断点 debug 一样。你还需要的另一个认知就是,选择器越精确,优先级就越高。这里可以解释一下,为什么 id 选择器的优先级要高于 class 选择器,因为 id 我们默认是唯一的,而 class 却可以作用与多个元素,所以相对于 id 来说范围很广。同理,为什么通常多个嵌套选择器的组合优先级要高于单个选择器,也是因为指向更明确嘛。

  • 源代码顺序

    后来者居上,具有相同优先级的规则集,源码位置靠后的会覆盖掉前面的相同属性的样式

  • !important

    这个属性标记要慎用,它会将当前属性的优先级提高到一个“很高”的level

这里有两条经验法则:

  1. 尽量不要使用 id 选择器
  2. 尽量不要使用 !important

要覆盖一个高优先级的规则,就得寻找更高优先级的方法,写代码不是搞“军备竞赛”,实现功能的前提下,也要考虑一下未来的扩展性

这里补充一个容易被忽略的“坑”点 —— 关于 a 样式的书写顺序:

:link => :visited => :hover => :active ,尝试调换它们的顺序,你会发现一些有趣的事情的。

理解继承

很好理解嘛,自己没有就拿父级的呗。不是所有的属性都支持继承,至于那些会被继承,自己查一下就好了。其实有一个最好的方法就是,你写一套明确作用于父元素的样式,如果子元素也呈现某些特征,那对应的属性就是会被继承的。

每个属性都可以将值设置为 initial 和 inherit,后者就是继承嘛,每什么好说的。前者比较有意思,将属性的值重置。注意,这个重置并不是应用默认样式表中的规则哦。

简写属性

类似于 font / background 这样的属性,可以写一大堆的值,但有时候我们只需要改变某些特征。这里就会出现一个问题,那就是你没有写的那些规则,也就是没有写全的时候,它会有一个 initial 值,没错,又是它,魔鬼就藏在细节当中。所以,如果不是样一次性应用很多的样式规则,那还是用一些精确指向的属性吧,比如 font-size / background-color 等。

总结

对于今天这篇文章,你只需要有这样的认知就够了:样式不生效,那就是被其他优先级更高的样式覆盖了,学会在控制台找到现在的样式特征是在哪里定义的,再根据特定的方法论(规则)去找到解决问题的办法


Date:2025/05/24

Author:kaiven