1. 为什么选择器如此重要?
选择器是CSS的核心组成部分,它决定了样式规则应用于哪些HTML元素。一个选择器写得不好,不仅会导致样式难以维护,还可能引起性能问题。
在大型项目中,选择器的质量直接影响代码的可读性和可维护性。我曾经维护过一个老项目,由于选择器写得非常复杂(像.container .content .left .box .title a.active
这样的嵌套选择器导致难以维护),最后不得不花费大量时间重构样式表。
2. 基础选择器
2.1 元素选择器
也称为类型选择器,通过HTML元素名称进行选择。
CSS
/* 选择所有<p>元素 */
p {
color: blue;
line-height: 1.6;
}
2.2 类选择器
通过元素的class属性进行选择,使用点号(.)前缀。
html
/* 选择所有class="special"的元素 */
.special {
background-color: yellow;
border: 1px solid #ccc;
}
<p class="highlight important">这段文字会受影响</p>
<p class="highlight">这段文字不会受影响</p>
<p class="important">这段文字也不会受影响</p>
/* 选择多个class的元素 */
.highlight.important {
font-weight: bold;
}
2.3 ID选择器
通过元素的id属性进行选择,使用井号(#)前缀。
CSS
/* 选择id="header"的元素 */
#header {
font-size: 1.5rem;
margin-bottom: 1rem;
}
最佳实践提示:ID选择器的特异性极高,应谨慎使用。一般来说,只在需要JavaScript频繁操作或非常独特的组件上使用ID。
2.4 通配符选择器
使用星号(*)匹配任意元素。
CSS
/* 移除所有元素的默认边距 */
* {
margin: 0;
padding: 0;
}
/* 不推荐:性能较差 */
.container * {
color: red;
}
性能警告:通配符选择器在文档中匹配所有元素,性能较差,应避免过度使用。
2.5 属性选择器
根据元素的属性或属性值进行选择。
html
<a href="#" target="_blank"> <!-- 会生效 -->
<a href="#"> <!-- 不会生效 -->
/* 选择有target属性的<a>元素 */
a[target] {
color: purple;
}
<a href="https://example.com"> <!-- 会生效 -->
<a href="http://example.com"> <!-- 不会生效 -->
/* 选择href属性以"https:"开头的<a>元素 */
a[href^="https:"] {
font-weight: bold;
}
<div class="alert warning"> <!-- 会生效 -->
<div class="warning-text"> <!-- 会生效 -->
<div class="alert"> <!-- 不会生效 -->
/* 选择class属性包含"warning"的元素 */
[class*="warning"] {
color: orange;
}
<p data-lang="zh-CN"> <!-- 会生效 -->
<p data-lang="en-US"> <!-- 不会生效 -->
/* 选择data-lang属性为"zh-CN"的元素 */
[data-lang="zh-CN"] {
font-family: "Microsoft YaHei", sans-serif;
}
3. 组合选择器
3.1 后代选择器
使用空格分隔,匹配指定元素内的所有后代元素。
CSS
/* 选择<div>内的所有<p>元素 */
<div>
<p>这个会变绿色(直接子级)</p> <!-- ✅ 生效 -->
<section>
<p>这个也会变绿色(孙子级)</p> <!-- ✅ 生效 -->
<div>
<p>这个同样会变绿色(曾孙级)</p> <!-- ✅ 生效 -->
</div>
</section>
</div>
<p>这个不会变绿色(不在div内)</p> <!-- ❌ 不生效 -->
div p {
c
}
/* 选择<div class="content">内的所有<span>元素 */
<div class="content">
<span>字体变大(直接子级)</span>
<p><span>嵌套的span也变大</span></p>
</div>
div.content span {
font-size: 1.2rem;
}
3.2 子选择器
使用大于号(>)分隔,仅匹配直接子元素。
CSS
<div>
<p>这个会生效(直接子级)</p> <!-- ✅ 生效 -->
<section>
<p>这个不会生效(孙子级)</p> <!-- ❌ 不生效 -->
</section>
<div>
<p>这个也不会生效(嵌套div中的p)</p> <!-- ✅ 生效 -->
</div>
</div>
<p>这个不会生效(不在div内)</p> <!-- ❌ 不生效 -->
/* 选择<div>的直接子元素<p> */
div > p {
margin-bottom: 1rem;
}
/* 比后代选择器更精确 */
.container > .header {
padding: 1rem;
}
3.3 相邻兄弟选择器
使用加号(+)分隔,匹配紧接在指定元素后的元素。
CSS
<h2>标题1</h2>
<p>这个会生效(紧跟在h2后面)</p> <!-- ✅ margin-top: 0 -->
<p>这个不会生效(不是紧挨着的)</p> <!-- ❌ 不受影响 -->
<h2>标题2</h2>
<div>其他内容</div>
<p>这个也不会生效(中间有其他元素隔开)</p> <!-- ❌ 不受影响 -->
<h2>标题3</h2>
<!-- 这里没有紧跟着的p元素 --> <!-- ❌ 没有匹配元素 -->
/* 选择紧接在<h2>后的<p>元素 */
h2 + p {
margin-top: 0;
}
/* 实现常见的卡片设计 */
.card + .card {
margin-top: 1.5rem;
}
3.4 通用兄弟选择器
使用波浪号(~)分隔,匹配指定元素后的所有兄弟元素。
CSS
<div>
<h2>标题</h2>
<p>这个会生效(在h2后面)</p> <!-- ✅ color: gray -->
<div>其他内容</div>
<p>这个也会生效(在h2后面)</p> <!-- ✅ color: gray -->
<p>这个也会生效</p> <!-- ✅ color: gray -->
</div>
<p>这个不会生效(不在同一个父元素)</p> <!-- ❌ 不受影响 -->
<h2>另一个标题</h2>
<!-- 这里没有p元素 --> <!-- ❌ 没有匹配元素 -->
/* 匹配<h2>后的所有<p>兄弟元素 */
h2 ~ p {
color: gray;
}
/* 实现选项卡内容切换效果 */
.tab-control ~ .tab-content {
display: none;
}
4. 结构与伪类选择器
4.1 伪类选择器
匹配元素的特殊状态。
css
<CSS>
/* 鼠标悬停 */
a:hover {
text-decoration: underline;
}
/* 获得焦点 */
input:focus {
border-color: blue;
box-shadow: 0 0 5px rgba(0, 0, 255, 0.5);
}
/* 激活状态 */
button:active {
transform: scale(0.98);
}
/* 链接访问状态 */
a:visited {
color: purple;
}
4.2 结构伪类
根据元素在文档中的位置匹配元素。
css
<CSS>
/* 第一个子元素 */
li:first-child {
font-weight: bold;
}
/* 最后一个子元素 */
li:last-child {
border-bottom: none;
}
/* 特定子元素 */
li:nth-child(3) {
color: red;
}
/* 奇偶行样式 */
tr:nth-child(odd) {
background-color: #f2f2f2;
}
/* 匹配父元素下唯一的子元素 */
p:only-child {
font-size: 1.2rem;
}
/* 匹配空元素 */
div:empty {
display: none;
}
5. 选择器特异性计算
当多个选择器应用于同一元素时,浏览器会根据特异性规则确定应用哪个样式。理解特异性规则对于解决CSS冲突问题至关重要。
5.1 特异性值计算规则
选择器类型 | 特异性值 |
---|---|
行内样式 | 1000 |
ID选择器 | 100 |
类、属性、伪类 | 10 |
元素、伪元素 | 1 |
通用选择器、组合符 | 0 |
5.2 特异性计算示例
css
<CSS>
/* 特异性为1 (元素选择器) */
div {
color: red; /* 胜胜 */
}
/* 特异性为10 (类选择器) */
.important {
color: blue; /* 胜胜 */
}
/* 特异性为100 (ID选择器) */
#header {
color: green; /* 胜胜 */
}
5.3 如何解决特异性冲突
css
<CSS>
/* 问题:样式被其他高特异性选择器覆盖 */
.container .content p {
color: red; /* 被覆盖 */
}
/* 解决方案1:增加特异性 */
.container .content .article p {
color: red; /* 胜胜 */
}
/* 解决方案2:使用!important(不推荐) */
.container .content p {
color: red !important; /* 不推荐使用 */
}
/* 解决方案3:重构选择器 */
.article-text {
color: red; /* 最佳方案 */
}
6. 实用技巧
6.1 精准定位
sql
<CSS>
/* 使用组合选择器减少不必要的特异性 */
.button-primary {
/* 胜胜胜 */
}
/* 而不是 */
.site-header .nav .button.primary {
/* 胜胜胜胜 */
}
6.2 BEM方法辅助选择器
sql
<CSS>
/* 使用BEM命名规范 */
.block {}
.block__element {}
.block--modifier {}
/* 实际使用 */
.card {}
.card__title {}
.card--featured {}
6.3 使用:not()简化选择器
css
<CSS>
/* 传统方法 */
li {
/* ... */
}
li:last-child {
border-bottom: none;
}
/* 使用:not()简化 */
li:not(:last-child) {
border-bottom: 1px solid #ddd;
}
7. 性能考虑
选择器的性能影响在大型项目中尤为明显。以下是优化选择器性能的一些关键点:
- 避免过长的选择器链:深度嵌套选择器会增加计算时间。
- 减少通用选择器的使用 :
*
选择器会匹配所有元素,性能较差。 - 限制使用标签选择器的数量:在类选择器前使用标签选择器增加了特异性,但没有提高性能。
- 避免过度使用后代选择器:后代选择器会遍历整个DOM树查找匹配。
css
<CSS>
/* 低效 */
.container div p span a {
/* 样式 */
}
/* 高效 */
.link {
/* 样式 */
}