伪类是css中用于匹配元素特定状态的选择器,必须写在css中而非html里;它不修改dom结构,仅在满足条件(如:hover、:focus)时动态应用样式。

伪类不是“加”在 HTML 标签上的,而是用 CSS 选择器匹配元素的特定状态;你没法通过写 <div :hover> 这种语法来添加它——浏览器根本不认。
<h3>伪类必须写在 CSS 里,不能写在 HTML 中</h3>
<p>很多人卡在第一步:试图在 HTML 标签里直接写 <code>:hover、:focus 或 :checked,比如:<button :active>点我</button>。这完全无效,HTML 解析器会忽略冒号后的内容,当普通属性名处理(甚至可能触发自定义属性警告)。
正确路径只有一条:保持 HTML 干净,把状态逻辑交给 CSS:
- HTML 只负责结构和语义,例如:
<input type="checkbox" id="agree"> - CSS 负责响应状态,例如:
input#agree:checked { background: #007acc; } - 浏览器自动监听用户交互(点击、悬停、聚焦等),并实时切换匹配的伪类样式
:hover 在移动端经常不生效?别硬扛,要换思路
移动端没有“悬停”概念,:hover 的触发依赖于模拟指针(如 Safari 的短暂 hover 状态),但多数安卓浏览器或 WebView 直接忽略它,或仅在点击后短暂生效。这不是 bug,是规范行为。
如果你的目标是“点击反馈”,应该优先用 :active;如果需要长期高亮态,得靠 JS 切 class:
-
button:active { transform: scale(0.98); }—— 点击瞬间压缩,兼容所有设备 - 需要“按住不放就一直变色”?
:hover+:active组合在桌面端有效,但移动端建议改用button.pressed { background: #1890ff; },再用touchstart/touchend控制 class - 不要依赖
:hover做关键功能入口(比如下拉菜单),否则 iOS 用户根本点不开
::before 和 ::after 死活不显示?先查这三件事
伪元素不渲染,90% 是因为漏了最基础的条件。不是浏览器问题,也不是 CSS 写错位置,而是三个硬性门槛没过:
- 必须声明
content属性,哪怕只是content: "";—— 没它,整个伪元素节点不会生成 - 父元素不能是自闭合标签:
<img>、<input>、<br>等无法承载伪元素,它们没有“内容流”可插入 - 若想定位覆盖内容(比如角标、遮罩),父元素得有
position: relative(或其它非static值),否则position: absolute的伪元素会相对于 body 偏移
例如这个常见错误写法:
span::after { content: "✓"; color: green; } —— 看似没问题,但如果 span 是空的(<span></span>),且没设 display: inline-block 或宽高,它可能被折叠到看不见。
伪类和伪元素混用时,顺序和层叠容易翻车
当你同时用 a:hover::after 或 li:nth-child(2):focus::before 这类组合选择器时,渲染顺序和 z-index 行为很具体:
- 伪元素默认是
inline级别,插在文字流里;一旦加了position: absolute,它就脱离文档流,但依然受父元素的z-index上下文限制 -
:hover::after的样式优先级 =:hover选择器权重 +::after权重,比单纯::after高,但不会高于带!important的普通规则 - 多个伪元素(如
::before+::after)默认按 DOM 顺序堆叠:::before在底层,::after在上层;想反着来?得显式设z-index
真正难调试的,往往是伪元素叠加在 SVG 或 canvas 上时的层叠失效——这时候不是 CSS 写错了,而是父容器缺少 transform: translateZ(0) 或 will-change: transform 触发新层叠上下文。











