- Published on
CSS选择器 & 优先级
- Authors
- Name
- 青雲
CSS选择器是前端开发中不可或缺的部分,它允许开发者精确控制页面中元素的样式。选择器的正确使用关乎网页的美观、用户体验以及维护的方便性。了解CSS选择器的种类以及优先级能够帮助我们写出更清晰、更高效的代码。
CSS选择器简介
CSS选择器定义了应当匹配哪些元素,并对这些元素应用定义的样式。根据选择器的不同,可以选择一个元素、一组元素,或者某些特定情况下的元素。 CSS提供了多种选择器,大致可以分为以下几类:
基本选择器
- 类型选择器:通过节点名称匹配元素。
p {
color: blue;
}
- 类选择器:(如.classname)按CSS类名匹配元素。
.classname {
font-weight: bold;
}
- ID选择器:(如 #idname)使用ID属性选择单一元素。
#uniqueId {
background-color: yellow;
}
- 通配选择器:(如 *)匹配所有元素。
* {
box-sizing: border-box;
}
属性选择器
- 选择具有特定属性或属性值的元素
input[type="text"] {
border: 1px solid gray;
}
伪类选择器
- 动态伪类(如 :hover)根据用户与元素的交互状态选择元素。
- 目标伪类(如 :target)选择当前活动的目标元素。
- 语言伪类(如 :lang())基于元素的语言进行选择。
a:hover {
text-decoration: underline;
}
伪元素选择器
- ::before 和 ::after 在元素内容前后插入内容。
- ::first-line 和 ::first-letter 分别用于选择元素的第一行或第一个字母。
p::first-line {
font-variant: small-caps;
}
组合选择器
- 后代选择器(如 div span)选择某元素内部的所有特定后代元素。
- 子选择器(如 div > p)仅选择直接子元素。
- 相邻兄弟选择器(如 h1 + p)选取紧接在另一元素后的元素。
- 后续兄弟选择器(如 h1 ~ p)选取同一父元素下的所有特定兄弟元素。
div span {
color: green;
}
div > p {
list-style-type: none;
}
h1 + p {
margin-top: 0;
}
h1 ~ p {
color: red;
}
选择器优先级
选择器的优先级是根据它的特殊性计算的。每个选择器都会根据其类型被赋予一个“权重”,并按照特定的计算规则进行累加,形成一个权重值,这个值将决定最终样式的应用。 特定性值是一个由四个部分组成的值,通常不作为实际的数字,而是作为逗号分隔的四个不同的部分来理解,例如 (0, 1, 0, 0)。
- 样式规则的权重:
- 内联样式(如在HTML元素的 style 属性内定义的样式)算作 (1,0,0,0)。
- 每个ID选择器增加 (0,1,0,0)。
- 每个类选择器、属性选择器或伪类增加 (0,0,1,0)。
- 每个类型选择器或伪元素增加 (0,0,0,1)。
- 样式声明中
!important
的作用:- 声明中加上
!important
会覆盖上述任何具体性计算结果,迫使样式优先应用。
- 声明中加上
- 继承和默认优先级:
- 继承得到的样式通常有较低的优先级。
- 用户代理(浏览器)的默认样式通常优先级最低。
假设我们有如下HTML代码:
<div id="toolbar">
<!-- E: 选择器内联样式,权重为 (1,0,0,0) -->
<button class="button active" style="background-color: orange">
Click me!
</button>
</div>
对应的CSS样式如下:
/* A: 选择器类型选择器,权重为 (0,0,0,1) */
button {
background-color: blue;
color: white;
}
/* B: 选择器类选择器,权重为 (0,0,1,0) */
.button {
padding: 10px 20px;
font-size: 14px;
}
/* C: 选择器类伪类,权重为 (0,0,2,0) */
button.active {
background-color: green;
}
/* D: 选择器ID选择器和类选择器,权重为 (0,1,1,0) */
#toolbar .button {
border: 10px solid red;
}
现在我们来计算每个选择器的优先级来决定最终button元素的样式:
选择器 | 特殊性 | 特殊性值(简写) | 样式 |
---|---|---|---|
A: button | (0,0,0,1) | (1) | **background-color: blue; |
color: white;** | |||
B: .button | (0,0,1,0) | (10) | **padding: 10px 20px; |
font-size: 14px;** | |||
C: button.active | (0,0,1,1) | (11) | background-color: green; |
D: #toolbar .button | (0,1,1,0) | (110) | border: 1px solid red; |
E: (inline) | (1,0,0,0) | (1000) | background-color: orange; |
根据上述分析可得:
- background-color: 由内联样式选择器E决定,因为它有最高的特殊性。
- color: 除了选择器A,没有其他选择器应用 color 属性,因此最终颜色是 white。
- padding 和 font-size: 由选择器B决定,因为没有其他选择器应用这些属性。
- border: 由选择器D决定,因为其特殊性高于其他选择器。
因此,button 元素的最终样式将会是:
{
background-color: orange; /* 来自内联样式 */
color: white; /* 来自选择器A */
padding: 10px 20px; /* 来自选择器B */
font-size: 14px; /* 同上 */
border: 1px solid red; /* 来自选择器D */
}
优先级的实际应用
在实际项目中,管理好样式优先级可以防止意外的样式覆盖和冲突。以下是一些常见的管理样式优先级的方法:
- 重置样式:通常在项目开始时应用全局样式重置(如使用 normalize.css 或 reset.css),建立一致的基线。
- 样式冲突解决:对于冲突,使用更具体的选择器来解决优先级问题,或者重新考虑选择器的结构和样式定义方式。
- 避免
!important
:尽量避免使用!important
,因为它会破坏样式的自然层叠和优先级逻辑,创建难以维护的代码。
最佳实践
- 编码规范:制定一致的编码规范和命名规范。
- 合理的样式组织:组织样式表结构,使之按组件或功能模块化。
- CSS预处理器:通过如Sass或Less等CSS预处理器,使用嵌套规则、变量和函数来灵活控制样式应用,简化选择器复杂度。
CSS选择器的性能影响
虽然影响微小,但选择器的复杂性确实可以影响到页面的渲染性能。以下是一些优化选择器性能的建议:
- 尽可能使用简单的选择器。
- 避免使用过多的子选择器和相邻选择器。
- 有意识地组织样式表,避免重复选择器。
- 使用开发者工具来审查和分析CSS性能。
排查CSS性能的常用方法
- Performance面板
- 打开Performance面板,点击录制按钮,然后重新加载页面或执行页面的特定操作。
- 停止录制并查看记录的时间线,确定CSS相关的渲染事件。
- 分析Layout和Paint,这两种活动通常与CSS渲染性能相关—Layout指的是计算元素几何位置的过程,而Paint是实际填充像素的过程。
- Coverage面板
- 使用命令菜单【ctrl+shift+p】打开菜单,输入coverage,选择coverage面板
- 点击开始录制,重新加载页面,或者浏览网页以确保各种交互被覆盖。
- 停止录制并查看结果,这将显示出多少字节的CSS资源未被使用。
- 利用Elements面板
- 在开发者工具中,选择“Elements”标签页,选择页面上的一个元素。
- 在右侧面板中,切换到“Computed”标签。这里可以看到应用到选中元素上的所有计算样式,包括从哪些规则继承而来。
- 右键点击特定的DOM元素,选择“Force state”可以模拟伪类如 :hover 的状态,查看这些状态变化是否导致性能问题。
面试实战
!important
在CSS中的作用,并解释为什么通常不推荐使用?
问题1 描述 答案:
!important
是一个CSS声明的后缀,它可以添加到样式属性的最后,来覆盖页面内正常的CSS层叠规则。任何标记为 !important
的样式声明,几乎都会覆盖同一元素上其他任何未标记为 !important
的声明。不推荐使用 !important 是因为它会破坏CSS的自然层叠规则,使得样式维护变得困难,尤其是在跨组件或者跨团队合作时,可能会发生意外的样式覆盖,并且增强了代码的脆弱性。
面试题 2: 以下哪个选择器的优先级最高?为什么?
div#app
#app
body #app div
div.container
答案:
div#app
的优先级最高。因为它既包含了ID选择器(权重为1),也包含了类型选择器(权重为1),所以它的具体性值是(0,1,0,1)。#app
仅仅包含ID选择器,具体性值是(0,1,0,0)。body #app div
包含一个ID选择器和两个类型选择器,具体性值是(0,1,0,2),但由于ID选择器具有较高的权重,这个选择器的优先级仍然低于div#app
。div.container
中包含一个类选择器和一个类型选择器,具体性值为(0,0,1,1),优先级最低。
问题3: 子元素会继承父元素的哪些CSS样式?继承的样式如何与CSS选择器特殊性相抗衡?
答案:
子元素会自然继承一些样式属性,例如 font-family
, color
和 visibility
,但不会继承那些关于布局的属性,例如 border
, margin
, background-color
等。如果对子元素明确设置了样式,则该样式根据层叠顺序和特殊性规则优先应用。继承的样式具有最低的特殊性权重,等同于类型选择器,通常是 (0,0,0,0);除非被更高特殊性的CSS规则覆盖,否则继承的样式会被应用。
面试题 4: 什么是伪元素,它们与伪类有什么不同?
答案:
伪元素是用于添加装饰或特殊效果的选择器,它们表示元素的某个部分,如 ::before 和 ::after 可以用来在元素内容的前后添加内容。伪类则是反映元素在所处上下文中的特定状态的选择器,例如 :hover、:active 和 :visited。伪元素和伪类的主要区别在于它们的应用:伪元素创建元素的部分,而伪类描述元素的特定状态。
问题5: 当两个相同权重的CSS选择器应用于同一元素时,哪一个会生效?
答案:
如果两个选择器的特殊性完全相同,那么位于CSS文件较后位置的选择器将会生效,因为CSS的层叠规则是根据源码顺序来处理权重相同的规则的。这意味着最后解析的规则将覆盖先前相同权重的规则。