跳到主要内容位置

为什么你的css无法生效?

恼人的改组件样式的背后是什么?

今天水群时看到群友有一些问题,他们在聊为组件修改属性时样式不生效。样式不生效的原因可能有很多,比如: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"。


稳扎稳打,向风生长;我是应怜,下次见。如果这篇文章对你有帮助,各位彦祖、亦菲点个关注再走呗。