移动端无mouseout事件导致:hover悬停弱化,需用@media (hover: hover) and (pointer: fine)精确过滤触控设备,并将transition包入其中;:active需满足特定条件才生效,js模拟is-hovered类须防双击缩放、滑动未触发及跳转污染。

因为移动端没有 mouseout 事件,浏览器只模拟一次 :hover 触发,却不负责清除 —— 这不是 bug,是规范允许的“悬停弱化”行为。
@media (hover: hover) and (pointer: fine) 必须这么写
单独写 @media (hover: hover) 在 iPad、Surface Pro 等设备上恒为 true,根本起不到过滤作用。这些设备既上报 hover: hover,又上报 pointer: coarse(手指输入),结果就是下拉菜单点开就卡住。
-
@media (hover: hover) and (pointer: fine)才表示“当前主输入方式支持悬停 + 足够精细”,自动排除所有手指触控场景 - 必须把
transition也包进这个媒体查询里,否则 iOS Safari 可能解析到但不触发动画 - 顺序必须放在常规样式之后,否则
.btn:hover会直接覆盖媒体查询内的定义 - 别写
@media (hover: hover) and (pointer: coarse)—— W3C 规范明确这两者互斥,这条永远不匹配
:active 在 iOS Safari 默认不生效
:active 是移动端最接近“按下反馈”的伪类,但它在 iOS Safari 中默认被禁用,除非满足以下任一条件:
- 元素是原生
<button></button> - 元素带
role="button"且有tabindex -
或其祖先绑定了ontouchstart(最简写法:)
另外,:active 动画持续时间建议 ≤ 0.15s,避免点击后有明显延迟感;别只改背景色,组合 transform: scale() 和 opacity 更可靠。
JS 模拟 is-hovered 类时三个高危坑
用 touchstart 添加 is-hovered 类看似简单,但实操中极易出错:
- 不调用
e.preventDefault()→ 安卓部分机型会触发双击缩放或滚动中断 - 在
touchend立即移除 class → 用户滑动时touchend不一定触发,推荐用setTimeout延迟 300ms 清除 - 元素有跳转行为(如
<a href></a>)→ 必须在click回调里立即清除is-hovered,否则新页面样式被污染
真正难的从来不是怎么写 :hover,而是怎么让它的边界在不同输入方式之间自然消失——它藏在 pointer: fine 的判定里,藏在 touchcancel 的监听里,也藏在你没加 tabindex 的那个 div 里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











