hover失效大概率是元素被遮挡,而非代码错误;需用DevTools检查渲染层、层叠上下文及pointer-events设置,并区分桌面与移动端交互逻辑。
position: absolute 或 fixed 元素 hover 不触发,90% 以上不是代码写错了,而是它在渲染层里被别的元素挡住了。浏览器根本没把鼠标事件派发给它——你悬停的位置,实际落在上层 DOM 节点上。
用 Chrome DevTools 的 Elements 面板按 Ctrl+Shift+C 悬停目标区域,看高亮框是否准确套在你要 hover 的元素上;再切到 Layers 面板(或勾选 Rendering → Paint flashing),确认该元素是否真被绘制在顶层。
常见遮挡源包括:
transform、opacity: 0.99、will-change 等隐式创建层叠上下文的属性,把子 fixed 元素“关”进局部 stacking contextdiv,样式未重置,z-index 却很高::before/::after 伪元素占满全屏但没设 z-index,成了隐形拦截层z-index 只对已定位元素(position: relative/absolute/fixed/sticky)有效,且只在同一个层叠上下文中比较大小。如果两个元素分别属于不同 stacking context(比如一个在 transform: translateY(0) 的容器里,另一个在根层),它们的 z-index 值互不相干。
立即学习“前端免费学习笔记(深入)”;
排查步骤:
position: relative; z-index: 1000; 临时测试,若 hover 恢复,说明是层级问题transform、filter、opacity < 1、will-change、isolation: isolate
body 下)当遮罩层必须存在(比如半透明背景、加载蒙层),又不能阻断下层交互时,pointer-events 是唯一可控手段。
关键规则:
pointer-events: none 必须加在**真正遮挡的元素上**,不是它的父容器pointer-events,所以 .overlay::before { pointer-events: none; } 才能让事件穿透pointer-events: none 不会继承,子元素需单独设置(除非你想整块禁用)pointer-events: auto 后又加 opacity: 0——透明≠不可交互,事件仍会被捕获iOS Safari 和多数安卓浏览器默认不持续响应 :hover,首次触摸后可能短暂激活一次,但这不是 bug,是规范行为。强行用 @media (hover: hover) 包裹只是让桌面端生效,对移动体验无实质提升。
真实项目中应:
:active + touch-action: manipulation 做按下反馈touchstart/pointerdown 添加 is-hovered 类,再配合 CSS 控制显隐pointer-events: none——这会导致 :active 也失效最常被忽略的是:你以为在调 z-index,其实元素压根没被渲染出来;或者你修好了桌面端 hover,却忘了移动端用户根本等不到那个“悬停”。