触控设备不支持:hover伪类的持续逻辑,因其无悬停物理状态;移动端仅在首次touchstart时模拟一次:hover,随后失效,且需@media (hover: hover) and (pointer: fine)联合判定才可靠。

因为触控设备没有“悬停”这个物理状态,浏览器压根不实现:hover伪类的持续逻辑——它不是bug,是规范行为。
移动端根本不存在:hover触发条件
鼠标有明确的“移入→悬停→移出”过程,而手指点下去就是激活,抬起来才是click。iOS Safari 和 Android Chrome 顶多在首次 touchstart 时模拟一次:hover(延迟约300ms),之后就再也不会响应。你看到
- 真机上点完按钮,
:hover样式卡住不退?那是浏览器把这次触摸标记为“悬停开始”,但没定义“悬停结束”事件 - 下拉菜单点开又瞬间收起?
.menu:hover .submenu在触控下只短暂匹配,松手即失效 - DevTools里
:hover规则被划掉?不是CSS写错,是伪类压根没被激活
@media (hover: hover) and (pointer: fine) 必须这么写
单独用@media (hover: hover)在iPad或Surface上恒为true,完全不可靠;@media (hover: none)则整条规则可能被忽略。真正有效的判定必须同时满足两个条件:
-
(hover: hover):当前输入方式支持悬停状态(鼠标/触控笔) -
(pointer: fine):指针精度足够高(排除手指、粗粒度触控笔) - 顺序必须放在常规样式之后,否则会被
.btn:hover直接覆盖 -
transition属性也得包进去,否则 Safari 可能解析到却不用
:active 在 iOS Safari 上默认不生效
:active本该提供按下反馈,但在 iOS Safari 默认不触发——除非你主动“唤醒”它:
- 给元素或其祖先绑定
touchstart事件(最简:) - 或让元素本身是
<button></button>、带role="button"且有tabindex="0" - 纯 CSS 的
cursor: pointer在移动端被忽略,无效 -
:active动画建议 ≤ 150ms;别只改背景色,组合transform: scale(0.98)+opacity: 0.9更可靠
JS 模拟 is-hovered 类时最容易漏掉三件事
用touchstart加类、touchend删类看似简单,但实操中常因细节缺失导致体验断裂:
- 必须调用
e.preventDefault(),否则安卓部分机型会触发双击缩放或滚动中断 - 别在
touchend立即移除类——用户滑动时touchend可能根本不触发,推荐setTimeout延迟300ms清除 - 如果元素有跳转行为(如
<a href="/xxx"></a>),要在click回调里立即清除is-hovered,否则新页面样式被污染
真正难的从来不是怎么写:hover,而是怎么让它在鼠标和手指之间自然切换边界——这藏在pointer: fine的判定里,也藏在你忘了给父容器加overflow: hidden导致位移被裁掉的那一行注释里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











