应使用@media (hover: none) and (pointer: coarse)判断纯触控场景,因其准确捕获“无悬停能力+粗粒度指针”状态;需配合JS监听pointerdown和matchMedia变化实现输入模式动态切换,并覆盖pen输入类型。
触摸屏设备(尤其是混合型设备如二合一笔记本、Surface)会同时报告 hover 和 any-hover,导致 CSS 媒体查询误判为“支持悬停”,从而隐藏关键交互提示(比如 tooltip、下拉菜单),用户点不中、找不到入口——这不是 bug,是规范行为,但必须干预。
@media (hover: hover) 在 iPad 或 Chromebook 上返回 true?CSS 规范要求设备只要“有能力悬停”(哪怕只是偶尔接上鼠标),就必须报告 hover: hover。iOS 13.4+、iPadOS、ChromeOS 都遵循该逻辑,哪怕当前纯触控操作,hover 仍为 true。
(hover: hover) 检查的是设备能力,不是当前输入方式(any-hover: hover) 同样不可靠,它只表示“存在某种可悬停的输入设备”,不反映当前状态(hover: none) and (pointer: coarse) 的组合,它更贴近“此刻只能触控”@media (hover: none) and (pointer: coarse) 替代单一 hover 判断这是目前最稳定、被 Safari、Chrome、Firefox 全面支持的组合条件,能准确捕获“无悬停能力 + 粗粒度指针(即触控)”场景。
(hover: hover) 做“仅桌面显示”的判断,它在 Surface 平板合盖+触控时仍为 true@media (hover: none) and (pointer: coarse) { .tooltip { display: block; } /* 触控设备默认显示提示 */ .nav-dropdown { display: block; } /* 展开一级菜单,取消 hover 触发 */}
pointerdown + matchMedia 动态降级纯 CSS 无法响应“用户从鼠标切到手指”的瞬间,需要 JS 协同。关键不是监听 touchstart(iOS 会延迟触发),而是用 pointerdown + matchMedia 监听变化。
立即学习“前端免费学习笔记(深入)”;
window.matchMedia('(hover: none)').matches 变化,比轮询高效pointerdown 且 event.pointerType === 'touch' 时,可临时标记“当前为触控模式”ontouchstart 全局事件,现代 iOS 已禁用该属性检测const hoverQuery = window.matchMedia('(hover: none)');let isTouchMode = hoverQuery.matches;<p>hoverQuery.addEventListener('change', e => {isTouchMode = e.matches;});</p><p>// 首次触控后强化判断document.addEventListener('pointerdown', e => {if (e.pointerType === 'touch') {isTouchMode = true;document.documentElement.setAttribute('data-input-mode', 'touch');}});
pen 输入类型Surface 等设备使用手写笔时,pointerType 是 pen,它既非 mouse 也非 touch,但同样不支持悬停。CSS 中 pointer: fine 会匹配笔,而 hover: none 通常也成立——所以你的触控适配逻辑要覆盖 pen。
e.pointerType === 'pen' 并归入触控逻辑分支(pointer: pen),因为 pen 属于 fine 类型,但 hover: none 已足够区分真正难处理的不是“有没有 hover”,而是“hover 能力存在但当前未激活”。所有基于能力的静态判断都可能滞后,必须结合 pointer 事件流与媒体查询变更事件做双保险。很多团队卡在只改 CSS 却不加 JS 监听,结果 iPad 上第一次点击失效——那是因为样式没及时切换,而用户已经划走了。