伪类选已有元素的状态或位置,伪元素在DOM外创建虚拟节点;伪类用单冒号,伪元素必须用双冒号;伪元素需content属性才渲染,:visited和::selection受浏览器限制。

伪类是“选状态”,伪元素是“造内容”
直接说结论:伪类(:hover、:focus、:nth-child())作用对象是**已有元素的动态状态或结构位置**;伪元素(::before、::first-letter、::selection)本质是**在 DOM 外创建并样式化一个虚拟节点**,它不对应 HTML 中的真实标签。
这个区别决定了它们的使用边界——你不能对 ::before 再加 :hover(除非它本身是可交互的容器,但那已是另一个元素了),也不能用 :first-child 去选中段落里“第一个字母”(那是 ::first-letter 的活)。
语法写错会失效,但浏览器可能“假装没看见”
现代 CSS 规范明确要求:伪类用单冒号 :,伪元素必须用双冒号 ::。虽然浏览器仍兼容 :before 这种旧写法(出于历史原因),但新伪元素如 ::marker、::backdrop、::highlight() 只认 ::,写成 :marker 就完全无效。
-
::selection必须双冒号,:selection在部分新版 Chrome/Firefox 中已不生效 -
:not()是伪类,永远只能是单冒号;写成::not()会直接被解析器忽略 - 混用写法(比如
a::hover)会导致整个选择器失效,且 DevTools 不报错——只是样式不应用
一个元素能叠加多个伪类,但伪元素有严格数量限制
你可以安全地组合伪类:input:focus:invalid 表示“获得焦点且验证失败”的输入框;a:link:hover 表示“未访问链接且鼠标悬停”。这些状态可以共存,CSS 引擎会逐层匹配。
立即学习“前端免费学习笔记(深入)”;
伪元素则不同:div::before 和 div::after 是允许共存的两个独立虚拟节点,但你无法写 div::before::first-letter(语法非法),也不能给同一个伪元素再套伪类(div::before:hover 无效,因为 ::before 生成的内容默认不可聚焦、无交互能力)。
注意:::first-line 和 ::first-letter 只对块级容器生效(如 p、div),对 span 或行内元素无效——这不是 bug,是规范限定。
实际开发中最容易踩的三个坑
不是概念记不住,而是写的时候一不留神就掉进兼容性/语义/渲染陷阱:
-
::before和::after必须带content属性才能渲染,哪怕只写content: "";漏掉它,整个伪元素就“消失”,DevTools 里也看不到 -
:visited受浏览器隐私策略限制,2023 年后 Chrome/Firefox 已禁止对其设置除color、background-color等极少数属性,设transform或display会被静默忽略 -
::selection在 Safari 中需加前缀::-webkit-selection才能生效,且无法继承父级颜色——必须显式声明color和background-color
真正卡住人的往往不是“不会用”,而是“用了但没效果,还找不到为什么”。盯着语法、content、浏览器前缀和隐私限制这三点查,能解决 90% 的伪类/伪元素调试问题。










