CSS 大海拾贝:从选择器优先级到层叠规则

46 阅读4分钟

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 层叠三要素

  1. 来源(Origin) :用户代理样式 < 用户自定义 < 作者样式(我们写的)
  2. 重要性(Importance)!important 最高(慎用!)
  3. 特异性(Specificity) :即“选择器优先级”

⚠️ 注意:层叠 ≠ 继承!继承是子元素自动获得父元素某些样式(如 colorfont),而层叠是解决冲突的规则。


三、选择器优先级:个十百千,谁说了算?

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"; /* → 箭头 */ }

配合 transformtransition,轻松实现悬停动画效果,无需 JS!


七、层叠上下文 & margin 重叠:隐藏的陷阱

7.1 Margin 重叠

  • 垂直方向的相邻块级元素 margin 会合并,取最大值
  • 解决方案:用 paddingborder、或创建 BFC(如 overflow: hidden

7.2 小数像素怎么处理?

浏览器会四舍五入到物理像素。例如 10.6px11px10.4px10px。在高 DPI 屏幕上可能更平滑。

7.3 inline 元素的 transform 限制

<span> 这类 inline 元素默认不支持 transform。解决方案:

  • 改为 inline-block
  • 或加 position: relative/absolute

八、最佳实践建议

  1. 避免过度嵌套.container .sidebar .menu li a
    改用语义化类名:.menu-link
  2. 慎用 !important:它会破坏层叠逻辑,后期难以维护
  3. 优先使用类选择器:比标签选择器更灵活,比 ID 更可复用
  4. 利用开发者工具:Chrome DevTools 的 “Computed” 面板能清晰看到最终生效的样式及来源
  5. 移动端适配:用 max-width + margin: 0 auto 实现响应式居中(见伪元素示例)