CSS 大海拾贝:从选择器优先级到层叠规则
“CSS 不是魔法,而是一门艺术——前提是你要理解它的规则。”
大家好!今天我们来聊聊前端开发中最基础也最容易被忽视的一环:CSS(Cascading Style Sheets)
一、何为 CSS?——不是“样式表”,而是“规则集合”
CSS 全称 Cascading Style Sheets,中文叫“层叠样式表”。但别被名字吓住,它本质上就是一套给 HTML 元素“化妆”的规则系统。
1.1 基本组成单位
- 声明(Declaration) :一个属性 + 值,比如
color: red; - 声明块(Declaration Block) :多个声明用
{}包起来 - 选择器(Selector) :告诉浏览器“这条规则要作用在谁身上”
- CSS 规则(CSS Rule) = 选择器 + 声明块
p {
color: blue; /* 这是一个声明 */
font-size: 16px;
} /* 整个是一个规则 */
二、CSS 的灵魂:层叠(Cascading)
“层叠”是 CSS 的核心机制,决定了当多个规则冲突时,谁胜出。
2.1 层叠三要素
- 来源(Origin) :用户代理样式 < 用户自定义 < 作者样式(我们写的)
- 重要性(Importance) :
!important最高(慎用!) - 特异性(Specificity) :即“选择器优先级”
⚠️ 注意:层叠 ≠ 继承!继承是子元素自动获得父元素某些样式(如
color、font),而层叠是解决冲突的规则。
三、选择器优先级:个十百千,谁说了算?
CSS 选择器优先级按 “个十百千” 计算(更准确说是四元组 (a, b, c, d)):
| 类型 | 权重 |
|---|---|
!important | 超越一切(不推荐滥用) |
行内样式 (style="") | 1,0,0,0 |
ID 选择器 (#id) | 0,1,0,0 |
类/属性/伪类 (.class, [attr], :hover) | 0,0,1,0 |
元素/伪元素 (p, ::before) | 0,0,0,1 |
通配符 *、组合符(+, >, 空格) | 0,0,0,0 |
🔍 实战分析:你提供的第一个例子
<div id="main" class="container">
<p>这是一个段落</p>
</div>
对应 CSS:
p { color: blue; } /* 0,0,0,1 */
.container p { color: red; } /* 0,0,1,1 */
#main p { color: green; } /* 0,1,0,1 */
✅ 最终颜色是 green!因为 #main p 的优先级(0,1,0,1) > .container p(0,0,1,1)
💡 小技巧:不要盲目加
!important,而是提高选择器特异性或调整结构。
四、常见选择器深度解析
4.1 属性选择器 & 伪类
你提供的第二个例子展示了强大的属性选择器:
[data-category="科幻"] { background: #007bff; }
[title^="入门"] h2::before { content: "🌟"; }
[attr=value]:精确匹配[attr^=value]:以 value 开头[attr$=value]:以 value 结尾[attr*=value]:包含 value
配合 ::before 伪元素,轻松实现“图标前缀”效果!
4.2 兄弟选择器 vs 后代选择器
第三个例子中:
h1 + p /* 紧跟 h1 的第一个 p */
p + p /* 紧跟另一个 p 的 p */
h1 ~ p /* h1 后所有兄弟 p */
.container > p /* 直接子元素 p */
.container p /* 所有后代 p */
✅ 关键区别:
>只选直接儿子- 空格选所有子孙
所以 <div class="inner"><p>内部段落</p></div> 会被 .container p 选中,但不会被 .container > p 选中。
五、:nth-child() vs :nth-of-type() —— 别再搞混了!
这是新手高频踩坑点!
<div class="container">
<h1>标题</h1>
<p>段落1</p>
<div>div1</div>
<p>段落2</p>
<p>段落3</p>
</div>
.container p:nth-child(5)→ 无效!因为第 5 个子元素是<div>,不是<p>.container p:nth-of-type(3)→ 选中“段落3” !因为它是在所有<p>中的第 3 个
✅ 记住:
:nth-child(n):看整体位置:nth-of-type(n):只看同类型元素中的位置
六、伪类 vs 伪元素:一个冒号还是两个?
| 类型 | 冒号数量 | 作用 |
|---|---|---|
| 伪类 | : | 描述元素状态(:hover, :focus, :nth-child) |
| 伪元素 | :: | 创建虚拟内容(::before, ::after, ::selection) |
你提供的伪元素示例中:
.more::before { /* 底部黄线 */ }
.more::after { content: "\2192"; /* → 箭头 */ }
配合 transform 和 transition,轻松实现悬停动画效果,无需 JS!
七、层叠上下文 & margin 重叠:隐藏的陷阱
7.1 Margin 重叠
- 垂直方向的相邻块级元素 margin 会合并,取最大值
- 解决方案:用
padding、border、或创建 BFC(如overflow: hidden)
7.2 小数像素怎么处理?
浏览器会四舍五入到物理像素。例如 10.6px → 11px,10.4px → 10px。在高 DPI 屏幕上可能更平滑。
7.3 inline 元素的 transform 限制
<span> 这类 inline 元素默认不支持 transform。解决方案:
- 改为
inline-block - 或加
position: relative/absolute
八、最佳实践建议
- 避免过度嵌套:
.container .sidebar .menu li a❌
改用语义化类名:.menu-link✅ - 慎用
!important:它会破坏层叠逻辑,后期难以维护 - 优先使用类选择器:比标签选择器更灵活,比 ID 更可复用
- 利用开发者工具:Chrome DevTools 的 “Computed” 面板能清晰看到最终生效的样式及来源
- 移动端适配:用
max-width+margin: 0 auto实现响应式居中(见伪元素示例)