:hover 在移动端无退出逻辑,需用@media (hover: hover) and (pointer: fine)精准识别悬停设备,并为触摸设备配:active反馈;iOS Safari需绑定touchstart或使用button元素才能触发:active。

别指望 :hover 在移动端自动退场——它压根没设计“退出”逻辑,点击后残留是浏览器的合规行为,不是 bug。真正有效的做法是隔离 + 替代:用媒体查询把 :hover 限定在真悬停设备上,再给触摸设备配一套独立反馈机制。
为什么 @media (hover: hover) 单独写会失效
单独写 @media (hover: hover) 在 iPad、Surface Pro 等混合设备上恒为 true,哪怕你正用手指点屏幕。它只说明“设备支持悬停”,不说明“此刻正在用鼠标”。结果就是下拉菜单点一下就展开又卡住。
- 必须叠加
(pointer: fine),写成@media (hover: hover) and (pointer: fine),才能准确筛出鼠标/触控笔场景 - 这个组合在 iOS Safari 13.4+、Chrome 82+ 支持,但微信 X5 内核、UC 浏览器直接忽略整块规则
- 规则必须放在常规样式之后,否则
.btn:hover会直接覆盖媒体查询里的定义 -
transition也得包进去,否则 Safari 可能解析到却不用动画
为什么 :active 在 iOS Safari 默认不触发
:active 是移动端最稳的瞬时反馈替代方案,但它在 iOS Safari 里默认被禁用,除非满足以下任一条件:
- 元素或其任意祖先绑定了
touchstart事件(最简写法:<div ontouchstart>) <li>元素是原生 <code><button></button>,或带role="button"且有tabindex="0" -
cursor: pointer在纯 CSS 中对移动端无效,不能靠它激活:active - 不调
e.preventDefault():安卓部分机型会触发双击缩放或滚动中断 -
touchend后立即移除 class 不可靠:用户滑动时touchend可能不触发,推荐用setTimeout(() => el.classList.remove('is-hovered'), 300) - 带跳转的元素(如
<a href></a>)要在click回调里立即清除is-hovered,否则新页面样式被污染
实操建议::active 动画持续时间控制在 0.15s 以内;别用 !important 覆盖 :hover,而是让 :active 自身定义完整状态(背景色 + 缩放 + 边框)。
立即学习“前端免费学习笔记(深入)”;
JS 模拟 is-hovered 类时最容易漏的三件事
监听 touchstart/touchend 手动切 class 看似简单,但真实项目里几乎必踩坑:
真正难的不是怎么写 :hover,而是怎么让它在鼠标和手指之间自然消失——它藏在输入设备的识别精度里,而不是在 CSS 规则顺序中。


















