伪类用单冒号、伪元素用双冒号是语法分界线;伪类修饰真实DOM元素状态(如:hover),伪元素创建虚拟内容(如::before)且必须含content或为特定文本片段;一个选择器仅能有一个伪元素但可叠加多个伪类。

伪类用单冒号,伪元素必须用双冒号
这是最直接、最不该出错的语法分界线。W3C 在 CSS3 中明确要求伪元素使用 ::(如 ::before),而伪类保持 :(如 :hover)。虽然浏览器仍普遍兼容旧写法(比如把 ::before 写成 :before),但混用会埋坑:
- 团队协作时容易误判意图——看到
:first-letter无法立刻判断是伪类还是伪元素 - 某些新特性(如
::backdrop)只支持双冒号写法,单冒号会失效 - 构建工具(如 PostCSS、stylelint)默认校验双冒号,报错后要返工
:hover 和 ::before 不能写在同一个选择器里
一个选择器只能有一个伪元素,但可以叠加多个伪类。这意味着你写 a:hover::before 是合法的,但 p::before::after 或 div::before:hover 都不合法。
-
a:hover:focus::before✅ —— 两个伪类 + 一个伪元素,顺序自由 -
li:first-child::before::after❌ —— 一个元素不能有两个同级伪元素 -
span::before:hover❌ ——::before是独立虚拟节点,它本身不响应:hover
想让伪元素响应悬停?得写成 span:hover::before,作用目标是原始 span 元素的状态。
伪类选“谁”,伪元素选“哪部分”或“加什么”
这个区别决定你该用哪个——不是凭感觉,而是看你要操作的对象是否真实存在于 DOM 树中:
立即学习“前端免费学习笔记(深入)”;
- 要样式化“第 3 个
button”?用button:nth-child(3)(伪类)——它仍是那个button元素 - 要在每个
button前加个图标?用button::before { content: "▶"; }(伪元素)——这个 ▶ 不在 HTML 里,是 CSS “造出来”的 - 要高亮用户选中的文字?用
::selection(伪元素)——DOM 中没有叫selection的标签,它是运行时抽象 - 要给已访问过的链接变灰?用
a:visited(伪类)——a元素本身没变,只是浏览器标记了它的状态
content 属性只对伪元素生效
content 是伪元素的身份证。只要用了 content,就一定是伪元素;反过来,没 content 的伪元素(如 ::first-line)也绝不会出现在伪类里。
-
p::before { content: "★"; }✅ —— 插入内容,典型伪元素 -
p::first-line { font-weight: bold; }✅ —— 不插入内容,但修饰局部,仍是伪元素 -
p:hover { content: "no"; }❌ ——content对伪类无效,会被忽略
容易被忽略的是:content: "" 也有意义——它让 ::before/::after 占位却不显示文字,常用来做 clearfix 或纯样式钩子。


















