一、从“伪类”到“伪元素”:概念澄清
很多人把 `:hover` 与 `::before` 混为一谈,前者是“伪类”,描述状态;后者是“伪元素”,描述结构。一句话:伪类选中已有元素在特定状态下的样式;伪元素则凭空生成一个不在 DOM 树中的盒子,再给它样式。理解这一点,就不会把 `::before` 当成“加在元素前面”的魔法,而是“在元素内部,创建一个位于内容之前的盒子”。
二、家族成员一览
1. `::before` 与 `::after`
最常见,用来生成装饰性内容或清除浮动。
2. `::first-line` 与 `::first-letter`
分别选中段落的首行或首字母,杂志排版常用。
3. `::selection`
用户选中文本时的背景与前景色,品牌定制神器。
4. `::marker`
列表项目前的符号,轻松换颜色或图形。
5. 实验性成员
`::backdrop`、`::placeholder`、`::cue` 等,按需渐进增强。
三、渲染原理:浏览器如何“造”盒子
当解析器遇到 `::before`,会:
- 在当前元素的内部,创建一个匿名行内盒子;
- 继承父元素的字体、颜色、行高,但可独立设置 `display`、`position`、`transform`;
- 该盒子与真实子元素同级,参与布局、回流、重绘。
因此,`::before` 可以清除浮动、可以绝对定位、可以旋转,但不能单独触发事件(因为它不在 DOM)。
四、性能考量:回流、重绘与层爆炸
- 回流:伪元素与父元素共用一个布局上下文,除非单独设置 `position:absolute/fixed`;
- 重绘:修改颜色、阴影、背景不会触发回流,可放心做动画;
- 层爆炸:大量 `::before` + `transform:translateZ` 会生成额外合成层,移动端需节制;
- 优化口诀:装饰用伪元素,交互用真实元素,动画慎用合成层。
五、语法细节:单冒号还是双冒号?
CSS2 时代写作 `:before`,CSS3 起推荐 `::before`,两者渲染一致,但语义更清晰。现代工具链默认输出双冒号,保持向后兼容。
六、实战场景:创意与实用并存
1. 图标系统
用 `::before` 注入 SVG 图标,实现“零 DOM”图标库。
2. 清除浮动
经典 `.clearfix::after { content:''; display:block; clear:both; }`。
3. 进度条
`::before` 宽度随 CSS 变量变化,实现纯 CSS 进度动画。
4. 文本高亮
`::selection { background:#ffcc00; }` 让品牌色无处不在。
5. 装饰角标
`::after` 绝对定位生成“NEW”角标,无需额外 HTML。
七、陷阱与误区
- 事件冒泡:伪元素无法绑定事件,需用父元素代理;
- z-index 陷阱:伪元素层级受父元素限制,需配合 `position:relative`;
- 可访问性:屏幕阅读器不朗读 `content` 文本,重要信息请放真实元素;
- 打印样式:伪元素不会出现在打印预览,需用 `@media print` 单独处理。
八、可访问性与语义
- 装饰性内容:放在伪元素,避免干扰语义树;
- 语义性内容:如按钮文字、表单提示,必须放回真实 DOM;
- 键盘焦点:伪元素无法聚焦,交互元素请用 tabindex。
九、渐进增强与降级策略
- 老旧浏览器:单冒号语法兼容,功能降级不影响布局;
- 实验特性:使用 `@supports` 检测 `::marker`,渐进增强;
- 打印样式:隐藏装饰性伪元素,避免打印浪费墨水。
十、未来展望:CSS 4 与伪元素
- `::highlight`:自定义选区颜色;
- `::target-text`:滚动到锚点时高亮文本;
- 容器查询 + 伪元素:响应式装饰角标、进度条。
十一、每日一练:亲手写一条装饰线
1. 需求:在卡片底部添加波浪线
2. 实现:
- `::after` 绝对定位
- `background-image` 波浪 SVG
- CSS 变量控制颜色
3. 测试:移动端 60 FPS 验证
4. 复盘:记录性能与可访问性
十二、结语:把伪元素当成设计系统
伪元素不是“奇技淫巧”,而是设计系统的一部分:
- 它让 HTML 保持语义纯净;
- 它让 CSS 承担装饰责任;
- 它让性能与可维护性兼得。
当你下一次面对“只需一个装饰角”的需求时,请想起这支隐形画笔——用一行 CSS,绘出无限可能。