为什么你的css无法生效?
2024年4月 · 预计阅读时间: 2 分钟
恼人的改组件样式的背后是什么?
今天水群时看到群友有一些问题,他们在聊为组件修改属性时样式不生效。样式不生效的原因可能有很多,比如:JavaScript 代码干扰、样式冲突、浏览器兼容性问题等等,这次我从选择器角度切入聊聊这个问题。
#
先来点简单的
html
// html <div class="container" id="home"> <h1>Welcome to my website</h1> </div>
// css .container { background-color: #f2f2f2; padding: 20px; text-align: center; }
上述代码比较基础,Welcome to my website这行字的背景色会显示为灰白色,这时候我调整一下。
// html <div class="container" id="home"> <h1>Welcome to my website</h1> </div>
// css .container { background-color: #f2f2f2; padding: 20px; text-align: center; } #home{ background-color: red; }
我加入了#home
,使得背景色改成了红色,这是最简单的样式代码了。这段代码在执行过程中,先是找到了对应的html元素,再给对应的元素赋予对应的样式属性。同时,由于选择器优先级的差异 ,原本应该是灰白色的背景变成了红色,我们也得到了一个事实,那就是在找元素的两种方式之间是存在冲突的。
这种寻找html元素的方式 被称为元素选择器,上述例子中一个 class 选择器,一个 id 选择器,它们都属于选择器;并且,选择器之间是分先后、分三六九等;也就是说,它们是有优先级差异的。
#
谁是大小王?
选择器的分类也比较多,我简单谈谈我的分法。
- 简单选择器
- 复合选择器
不同的选择器规则对应的优先级不一样,优先级更高的规则会直接覆盖了选择器低的规则。
#
简单选择器
顾名思义,它很简单(不是)。上述的代码中:id,class 都属于简单选择器。其中,id 选择器是大于 class 选择器的,哪怕我写一千个 container 的class,优先级不会高于 id 选择器。
>
其中有一个趣事,老版本的ie浏览器是可以做到这一点的,曾经有大神无聊到写了999个 class 选择器,使其权重超过了 id 选择器
简单选择器中还有一些别的,也是比较常用的类型,包含:属性选择器 、伪类选择器 、伪元素选择器 。这几种选择器的用法我们后续有机会再讲,先看看它们长啥样。
/* 使用属性选择器来选择具有特定class的div元素 */div[class="container"] { margin: 0 auto; width: 80%;}/* 使用ID选择器和伪类选择器来改变鼠标悬停在h1元素上时的颜色 */#home h1:hover { color: blue;}
/* 使用伪元素选择器来在h1元素前添加内容 */#home h1::before { content: "Hello! ";}
/* 使用伪元素选择器来在h1元素后添加内容 */#home h1::after { content: " Enjoy your stay.";}
#
复杂选择器
复杂选择器是多种简单选择器的组合,它们以多种不同类型的符号相连接。
我们也做一下基本的分类
- 后代选择器 :
div p
会选择<div>
内部的所有<p>
元素(不论是直接还是间接的后代)。 - 子元素选择器 :
div > p
会选择<div>
的直接<p>
子元素。 - 邻近兄弟选择器 :
h1 + p
会选择紧跟在<h1>
后面的<p>
元素(<h1>
和<p>
具有相同的父元素,且<p>
紧跟在<h1>
后面)。 - 一般兄弟选择器
h1 ~ p
会选择所有在<h1>
后面的<p>
元素(<h1>
和<p>
具有相同的父元素)。
同样的,复杂选择器+简单选择器,它能够实现更加精准的元素选择,但与之对应的代价是,用的第三方组件由于在多种选择器规则加持下,变得不容易更改它的样式 。这可能是辛苦写的样式不生效的罪魁祸首。
那么,为什么呢?
实际上,选择器的优先级,或者说权重,是由一套特殊的进制规则实现的,它在css 语义中被称为 specificity
。它在形式上看起来像是一个1000进制的数字,实际上,specificity(0,0,0,0) 的每一位都各自独立,且不会进位。一个元素会套上多种不同的规则,多种不同的规则对应的选择器在specificity 上又具有不同的权重,“进制”越大且“数值”越大的选择器,它的优先级就更高。
这一部分比较难理解,拿上述的简单选择器举例说明。
// html <div> <h1>About me</h1> </div> // css <style> h1 { font-size: 24px; } </style>
这段代码直接使用元素本身来选择,使用的是h1来赋予属性,它在选择器进制中对应的是(0,0,0,1),我们记为一分。
而 class 选择器的 specificity 是这样的:(0,0,1,0),如下
// html <div> <h1 class="text">About me</h1> </div> // css <style> h1 { font-size: 24px; } .text{ font-size:22px; } </style>
22号字会覆盖24号字。
我们继续
// html <div> <h1 class="text" id="me">About me</h1> </div> // css <style> h1 { font-size: 24px; } .text{ font-size:22px; } #me{ font-size:20px; } </style>
id 选择器的 specificity 是这样的:(0,1,0,0),它占据“百分位”的位。id 的20号字会把上述的两种字号都通杀。
最后再看一种
// html <div> <h1 class="text" id="me" style="font-size:12px;">About me</h1> </div> // css <style> h1 { font-size: 24px; } .text{ font-size:22px; } #me{ font-size:20px; } </style>
我没有在 style 内写样式了,而是直接通过内联的方式写入 html 结构内,这种内联的方式优先级最高,它的specificity 是这种形状:(1,0,0,0)
当多种规则同时加载一个元素上的时候,比如上述的h1,它们的specificity 会计分,最终对应的值是(1,1,1,1),1111的权重。当然,复杂选择器(多种组合)会在specificity 积分牌上累加数量,元素被选中的次数越多,对应位数的数值越大,并且,它在理论上不会进位,也就是说它是无限大的,实际上最大值应该是六万多。正常的开发web应用我想不会突破这样的界限,这样会使得规则相当的复杂。
总结
群友迷失在改组件的痛苦中,有很大一部分因素是组件库本身已经具备了相当的规则复杂度,要调整这样类型的组件不是易事。specificity 中也提供了一个 "any type",那就是!important ,当然了,我不建议在任何情况下使用 "any type"。
稳扎稳打,向风生长;我是应怜,下次见。如果这篇文章对你有帮助,各位彦祖、亦菲点个关注再走呗。