为何 css 写了不生效?

前言

前端开发有时候我明明写了 css 属性但是样式却没能在浏览器中生效,这其实很大概率原因就是你可能还不了解 css 属性值的计算过程,本期文章就带大家过一遍这部分知识,或许有你不清楚的内容🤷‍♂️

css 属性值的计算过程

CSS属性值的计算过程指的是浏览器渲染引擎将 CSS 属性从声明到最终渲染的处理流程,直白点就是 css 属性从没有值 到 有值的 过程。

css 属性并不是不写 css 就没有属性了,浏览器会有个 用户代理样式表 user agent stylesheet ,你可以理解为浏览器默认的样式

比如 我写一个 h1 元素,不给他写任何属性,默认就会有如下样式

这里你会发现 h1 默认样式中有个 display: block; 这其实就是为啥 h1 是一个块级元素,就是因为浏览器赋予了它这个属性,仅此而已,在 html 语义化出来之前你可以这样叫,什么块级元素都是 html5 之前的叫法,h5 出来之后官方摒弃了这个说法,如今你要知道的是这都是 css 属性默认值的效果

当我们展开 computed,你就会看到更多的属性,computed 就是这个属性最终计算出来的样式,实际上 computed 中有这个 元素 的所有样式属性

css 属性值的计算过程总览:

  1. 确定声明值
  2. 层叠
  3. 继承
  4. 使用默认值

第一步:确定声明值

这个会从两个表中获取,一个是作者样式表,一个就是上面提到的 浏览器默认样式表,其英文名为 user agent stylesheet,这里直译起来是用户代理样式表不方便理解。两个样式表的样式都是声明值

早期其实还有个 user stylesheet 用户样式表,这个样式表在早期的 ie 浏览器存在,在 IE 中,可以转到Tool> Internet Options> General Tab> Accessibility button> Accessibility Window> User style sheet section>"使用我的样式表格式化文档"复选框。其实就是用户在浏览器夹杂一些自己写的样式:参考stackoverflow css - 默认、用户和作者样式表有什么区别?_Stack Overflow中文网

作者样式表 就是我们写的 css 属性,若我们引入了 ui 库,这些 ui 组件的 样式也是 作者样式表

确定声明值 也有步骤:

  1. 找到 没有冲突 的样式,直接作为计算后的样式
  2. 将相对单位转成绝对单位

比如我给 h1 加一个 color 属性,color 在浏览器默认样式表中不存在,因此没有冲突,color 就会作为计算后的样式

浏览器默认样式表中的 h1 有一个 font-size: 2em; em 就是相对单位,它相对的是父容器的 字体大小,我这儿没有给父容器,因此相对的是浏览器的默认字体大小 16px,因此这里 h1 最终 computed 就是 32px 的 font-size。所以说假设我们给 h1 一个 父容器并且设置 font-size 15px,那么最终 h1 的font-size 就是 30px

相对单位不仅仅说的是 em,%,rem 这种,还包括了 font-weight: bold,color: red 等这种关键字,bold 就是对应 字重 700, red 就是对应 rgb(255, 0, 0)

第二步:层叠

层叠的目的就是一件事:解决冲突,这个过程才是最重要的

层叠有三个步骤:

  1. 比较重要性
  2. 比较特异性
  3. 比较源次序

先看重要性,重要性从高到低:

  1. 带有 important 的作者样式表
  2. 带有 important 的默认样式表
  3. 作者样式表
  4. 默认样式表

由于早期的 user stylesheet 已经不存在了,这里不做探讨

作者样式表 会覆盖 默认样式表 这很好说明

这里我在代码中写了 h1 标签的样式,默认样式表中的 font-size 就被覆盖了

这里我想要验证 带有 important 的默认样式表 会大于 作者样式表,但是始终没能成功,input 有个 属性 writing-mode,默认样式就有个 important 值,但是我写了不带 important 的 input 的 writing-mode 也能覆盖,这里有大佬清楚可以评论区补充下🚀。

第二步:比较特异性

特异性英文单词就是 specificity ,一般我们会说成权重

内联样式 ID选择器 类选择器、属性选择器、伪类 元素选择器、伪元素 通配符、关系选择器
权重:1000 权重:100 权重:10 权重:1 权重:0
style="color: red" #header .container [type="text"] :hover div ::before * > +

vscode 上 hover 样式选择器时会呈现最终的权重,不过由于不是写的内联样式,他只会给你呈现 后面三个值。

另外,mdn 上关于 css 权重有个 很形象的 图,这里可以看到 important 的权重是 10000

因此直观上感受,选择器内容越多,权重就会比较高

特殊伪类的权重计算

特殊伪类这里说的是 :is() 、:not() 、:where()

:is() 和 :not() 二者均是采用 参数中 最高特异性选择器的权重

如图这个例子,选取 item 中 值最高的 #id 作为最终权重,not 同理

:where() 权重始终为 0,无论参数中选择器的权重如何

where 的功能与 is 几乎相同,允许指定多个选择器,但是 where 权重始终为 0,这就是为了方便后续覆盖

为何要少用 !important

!important 权重是 10000,我们用它一时爽,后面维护起来就要骂娘了,主要是因为用 !important 会带来一些问题:

  1. 破坏 css 级联原则

    css 全称 就是 Cascading Style Sheets 层叠样式表,层叠一词就能直接体现 选择器 优先级的重要性,当我们对某个样式使用了 !important 时,它就会绕过这个机制,这样样式覆盖我们就无法预测

  2. 难以调试

    当多个 !important 冲突时,这个时候最终生效的样式只能是最后加载的样式,而非选择器权重,这样调试起来效率就很低

第三步:比较源次序

若前面的重要性一样,特殊性一样,那么最终就是源次序靠后的胜出

比如我在同一个选择器中对同一个 css 属性写了多次,那么最终只取最后一个值

css 复制代码
div h1.className {
    font-size: 10px;
    font-size: 20px;
}

这里最终就是 20px 的 font-size

这个例子过于简单了,这里我再举一个隐形一点的🌰

比如这里我写一个样式

css 复制代码
.parent {
    width: 400px;
    height: 400px;
    padding: 20px;
    border: 5px solid;
    background-clip: content-box;
    background: red;
}

background-clip 属性设置元素的背景是否延伸到边框、内边距盒子,内容盒子下面,content-box 就不会让背景色延伸到 padding,实际上这里最终效果全部覆盖了

这其实就是因为 background-clip 被覆盖了,background 属性展开可以看到里面具有 background-clip 属性,且用的是默认值 initial,在源次序中 initial 取胜

因此这里我们可以将 background-clip 属性放最后写,或者也可以直接写 background: red content-box

第三步:继承

第二步骤结束后,很多属性是没有值的,若这些属性 默认可以继承 ,则使用继承,

比如 font-size 容易继承,但是background-color 默认无法继承,默认无法继承可以用 inherit 作为 value 去手动继承

css 复制代码
.child {
	background: inherit;
}

第四步:使用默认值

若继承过后还有属性没有值,那么就会使用 css 属性的默认值,这就是 user agent stylesheet,比如 position 的默认值 为static

最后

了解这部分内容应该会对你 开发 css 调试上更容易些,但大概也不会让你写出更漂亮的界面,最终该写成啥样的还是啥样🥲

文章中若出现错误内容还请各位大佬见谅并指正。如果有任何问题或建议,欢迎指出,另外,有不懂之处欢迎在评论区留言。如果觉得文章对你的学习有所帮助,还请 关注、点赞、收藏 一键三连,感谢支持!欢迎关注我的公众号: Dolphin_Fung

相关推荐
PAK向日葵1 小时前
【算法导论】PDD 0817笔试题题解
算法·面试
加班是不可能的,除非双倍日工资3 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip4 小时前
vite和webpack打包结构控制
前端·javascript
excel4 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国5 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼5 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy5 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT5 小时前
promise & async await总结
前端
Jerry说前后端5 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化