移动端:hover不持续触发是因触控设备无悬停物理状态,浏览器无法区分点击与划过;iOS/Android仅首次触摸模拟hover,属规范行为而非bug。

为什么移动端:hover根本不会持续触发
因为触控设备没有“悬停”这个物理状态,浏览器无法判断手指是准备点击还是只是划过。iOS Safari 和 Android Chrome 会把首次触摸当作一次短暂的 hover 模拟,之后就不再响应——这不是 bug,是规范行为。你写的 .btn:hover { background: red; } 在真机上点一下闪一下,大概率就是这个原因。
@media (hover: hover) and (pointer: fine) 怎么写才不白费
单独写 @media (hover: hover) 在 iPad 或 Surface 上恒为 true,完全不可靠;@media (any-hover: hover) 更糟,连蓝牙鼠标都算进来。必须叠加精度判断:
- 只对鼠标、触控笔生效,自动排除所有手指触控
- 把所有依赖
:hover的样式(如下拉菜单、tooltip)包进这个媒体查询里 -
transition属性也得包进去,否则移动 Safari 可能解析到却不用 - 顺序要放在常规样式之后,否则会被
.btn:hover覆盖
:active 在 iOS Safari 上为啥不生效
:active 在 iOS Safari 默认不触发,除非满足以下任一条件:
- 元素或其祖先绑定了
touchstart事件(最简做法:<body ontouchstart="">) - 元素本身是
<button>或带role="button"且有tabindex="0" - 仅靠
cursor: pointer无效,纯 CSS 声明在移动端被忽略
另外,:active 动画持续时间建议 ≤ 0.15s;别用 !important 强行覆盖 :hover,而是让 :active 自身定义完整状态(背景、缩放、边框等)。
立即学习“前端免费学习笔记(深入)”;
用 JS 模拟 is-hovered 类时最容易漏掉的三件事
监听 touchstart/touchend 看似简单,但实操中常踩坑:
- 必须调用
e.preventDefault(),否则安卓部分机型会触发双击缩放或滚动中断 - 别在
touchend立即移除 class——用户滑动时touchend不一定触发,推荐用setTimeout延迟 300ms 清除 - 如果元素有跳转行为(如
<a href="/page">),要在click回调里立即清除is-hovered,否则新页面样式被污染
真正难的从来不是怎么写 hover,而是怎么让它的边界在不同输入方式之间自然消失——它藏在 pointer: fine 的判定里,也藏在你忘了给父容器加 will-change: transform 的那一行注释里。


















