这不是 bug,是浏览器对悬停状态的延迟清除机制——ios safari 和部分 android chrome 将首次触摸视为 :hover 起点,但无“手指移开”事件终结它;移动端无鼠标概念,:hover 为模拟行为,touchstart 后激活却不在 touchend 清除,依赖滚动或焦点切换重置;原生按钮更难控制,因自带高亮逻辑;x5 内核甚至忽略 :hover 更新;@media (hover: hover) 仅判设备能力,需搭配 (pointer: fine) 排除触控;:active 需绑定 touchstart 或设为 button/role="button"+tabindex 才生效;cursor: pointer 对移动端无效;js 模拟 is-hovered 易遇触摸穿透、滚动干扰与内存泄漏;最终须确保元素被识别为可交互(如 tabindex="0")。

这不是 Bug,是浏览器对悬停状态的延迟清除机制——iOS Safari 和部分 Android Chrome 把首次触摸当作 :hover 的起点,但没有“手指移开”事件来终结它。
为什么点击后 :hover 样式卡住不退
移动端没有鼠标移入/移出概念,:hover 在触摸设备上本质是“模拟兼容行为”。浏览器会在第一次 touchstart 后短暂激活 :hover,但不会在 touchend 时自动清除。它依赖外部事件(如滚动、焦点切换、点击其他区域)触发重置,而这些并不总发生,也不及时。
- 真机上点按钮背景色挂着,大概率就是悬停上下文没被释放
-
<button></button>比<div> 更难控制,因为原生控件自带高亮逻辑,和 CSS <code>:hover并行运行 - 微信/QQ 内置浏览器(X5 内核)甚至完全忽略
:hover状态更新,残留更顽固 - 必须写成
@media (hover: hover) and (pointer: fine),才能排除所有手指触控场景 -
@media (hover: hover) and (pointer: coarse)是无效组合,W3C 规范里二者互斥 - 该规则必须放在常规样式之后,否则会被普通
.btn:hover覆盖 - transition 属性也得包进同一媒体查询里,否则移动 Safari 可能解析到动画却无法触发
- 元素或其任意祖先绑定了
touchstart事件(最简做法:) - 元素本身是
<button></button>或带role="button"且有tabindex="0" -
cursor: pointer在纯 CSS 中对移动端无效,不能靠它激活:active - 触摸穿透:快速连点可能触发两次
touchstart,导致 class 未清除就重复添加 - 滚动干扰:页面滚动时触发
touchstart,误判为点击,样式异常激活 - 内存泄漏:单页应用中组件反复挂载/卸载,没做事件清理就会累积监听器
@media (hover: hover) 为什么单独用会失效
这个媒体查询只回答“设备是否支持悬停”,不回答“此刻是不是鼠标在操作”。iPad 接妙控板、Surface Pro 触控笔等设备会同时上报 hover: hover 和 pointer: coarse,导致你写的 .menu:hover { display: block; } 在手指点一下后就“卡开”。
:active 在 iOS Safari 上默认不生效的三个条件
:active 是移动端最稳的瞬时反馈替代方案,但它在 iOS Safari 默认被禁用,除非满足以下任一条件:
另外,:active 动画持续时间建议 ≤ 0.15s;别用 !important 强行覆盖 :hover,而是让 :active 自身定义完整状态(背景、缩放、边框等)。
JS 模拟 is-hovered 类的三类典型坑
监听 touchstart/touchend 手动切 class 看似灵活,实操中极易出错:
如果非要用,至少在 touchend 后加 setTimeout(() => el.classList.remove('is-hovered'), 0),避开 iOS 的 300ms click 延迟窗口;更稳妥的是用事件委托,统一在父容器监听,靠 event.target.matches('[data-hoverable]') 判断目标。
真正容易被忽略的点是:即使写了 @media (hover: hover) and (pointer: fine) 和 :active,如果元素没被浏览器识别为“可交互”,这些伪类依然不会触发。确保元素有 cursor: pointer 或 tabindex="0"(对非 <button></button> 的 <div> 尤其关键),且父容器不能包裹整个视口导致事件捕获异常。</div>











