为何 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

相关推荐
Pandaconda1 分钟前
【新人系列】Golang 入门(十五):类型断言
开发语言·后端·面试·golang·go·断言·类型
ohMyGod_1232 分钟前
React-useRef
前端·javascript·react.js
每一天,每一步5 分钟前
AI语音助手 React 组件使用js-audio-recorder实现,将获取到的语音转成base64发送给后端,后端接口返回文本内容
前端·javascript·react.js
上趣工作室5 分钟前
vue3专题1------父组件中更改子组件的属性
前端·javascript·vue.js
冯诺一没有曼9 分钟前
无线网络入侵检测系统实战 | 基于React+Python的可视化安全平台开发详解
前端·安全·react.js
getapi13 分钟前
flutter app实现分辨率自适应的图片资源加载
前端·javascript·flutter
—Qeyser42 分钟前
用 Deepseek 写的html油耗计算器
前端·javascript·css·html·css3·deepseek
萌萌哒草头将军1 小时前
VsCode Colipot 🚗 + MCP Tools ✈️ = 让你的编程体验直接起飞 🚀🚀🚀
前端·visual studio code·mcp
萌萌哒草头将军1 小时前
🚀🚀🚀MCP SDK 快速接入 DeepSeek 并添加工具!万万没想到MCP这么简单好用!
前端·javascript·mcp
拉不动的猪1 小时前
简单回顾下useMemo
前端·javascript·面试