伪类选择状态,伪元素生成内容;伪类用单冒号(如:hover),伪元素必须用双冒号(如::before);前者不改变dom,后者创建匿名渲染节点,且一个元素最多两个伪元素。

伪类选状态,伪元素造内容 —— 不是命名习惯问题,而是 DOM 是否真实存在、能否被 JS 操作的根本差异。
伪类用单冒号 :hover,伪元素必须用双冒号 ::before
语法不是可选项:CSS3 明确规定伪元素使用双冒号(::),单冒号(:)仅保留给伪类。虽然浏览器仍兼容 :before 这种写法,但它是历史遗留,不代表正确用法。
实际影响在于可维护性与团队协作:
-
a:hover和a:active是伪类,描述链接的交互状态,不改变 DOM 结构 -
a::before是伪元素,浏览器会为它生成一个匿名的、不可见于 HTML 的渲染节点 - 混用冒号(比如写成
a::hover或a:before)不会报错,但会让新人误判语义,也容易在代码审查中被标记
:first-child 和 ::first-letter 本质不同:一个选元素,一个选内容片段
前者匹配的是整个元素节点,后者匹配的是文本内容中的字符级位置 —— 它们根本不在同一抽象层级上。
典型误用场景:
- 想给段落首字加大加色,却写了
p:first-child→ 错,那是选“第一个p元素”,不是“每个p的第一个字” - 想高亮表格奇数行,却用了
tr::nth-child(odd)→ 错,::nth-child不存在,:nth-child()才是结构伪类 -
::first-letter只对块级元素生效;span::first-letter默认无效,需先设display: block
一个元素最多只能有两个伪元素:::before 和 ::after
这是 CSS 规范硬性限制,不是浏览器实现差异。你不能写 div::before::before,也不能叠加 ::marker 和 ::before 到同一个元素上(除非是不同上下文,比如 li::marker 和 li::before 共存,但它们作用目标不同)。
常见翻车点:
- 试图用多个
::before实现多层装饰 → 必须改用 wrapper 元素或 SVG - 在已用
::before插入图标的按钮上,又想用::first-line控制文字换行样式 → 冲突,::first-line对空内容或纯图标按钮无效 -
::selection是全局伪元素,不绑定到具体元素,也不受::before/::after数量限制影响
:is() 和 ::has() 正在模糊边界,但没改变底层逻辑
:is() 是伪类,用来简化选择器组合(如 :is(h1, h2, h3):hover);::has() 是实验性伪类(注意:不是伪元素),用于“父元素是否包含某子元素”的判断(如 article:has(> img))。
它们再怎么强大,也不会让伪类生成新节点,也不会让伪元素响应用户交互事件 —— 后者依然无法被 document.querySelector 获取,::before 插入的内容也拿不到 textContent。
真正容易被忽略的一点是:::before 和 ::after 的 content 属性为 none 或空字符串时,该伪元素不渲染,但其样式声明仍参与层叠计算 —— 这会导致意外的 z-index 或 margin 影响。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











