@media (hover: none) and (pointer: coarse)才是可靠组合,它同时要求设备无悬停能力且主指针为粗粒度(如手指),精准覆盖典型触屏交互场景,而单独@media (hover: none)仅反映悬停能力缺失,并非等同于“是触摸屏”。

@media (hover: none) 不能直接判断“是否为触摸屏设备”,它只表示当前设备**不具备悬停能力**——iPhone、iPad(默认)、多数 Android 手机和平板会匹配,但部分 Android 浏览器会错误报告 hover: hover,导致漏判。
为什么 @media (hover: none) 不等于“是触摸屏”
它反映的是设备能力模型,不是物理输入方式:
– Windows 触控笔记本(如 Surface)插着鼠标时仍报告 hover: hover,哪怕正用手指操作
– 某些 Android 浏览器(如旧版 Samsung Internet)误报 hover: hover,实际却是纯触控
– hover: none 匹配的是「无悬停能力」设备,但不包含所有触控场景;反过来,匹配不到也不代表就支持 hover
@media (hover: none) and (pointer: coarse) 才是可靠组合
这个组合能更贴近“此刻只能触控”的真实状态,被 Safari、Chrome、Firefox 全面支持:
– hover: none 排除有悬停能力的设备
– pointer: coarse 确认当前活跃指针是粗粒度(即手指)
– 二者同时成立,基本可认定为典型触控交互场景
- 适合用于:默认显示 tooltip、展开一级导航菜单、增大点击区域(
min-height: 44px) - 不要单独用
pointer: coarse当“移动端开关”——桌面触控屏、遥控器、游戏手柄也会触发它 - 避免写成
@media (hover: hover) and (pointer: coarse),W3C 规范中这两者互斥,永远不匹配
别用 'ontouchstart' in window 驱动 CSS 样式
这个 JS 检测返回 true 只说明浏览器注册了 touch 事件,不代表设备有物理触摸能力:
– Chrome 桌面版 DevTools 模拟器返回 true
– 某些 Linux 桌面浏览器也返回 true
– 它和 hover: none 不等价:Surface Pro 同时满足 'ontouchstart' in window === true 和 @media (hover: hover)
- CSS 分支必须靠媒体查询原生能力,而不是 JS 运行时结果
- 如果需要动态响应输入切换(比如 iPad 插上蓝牙鼠标),用
window.matchMedia()监听(hover: hover) and (pointer: fine)变化,再通过 JS 切换 class
真正难处理的是混合设备——用户可能在同一个会话里切换输入方式。媒体查询本身是静态的,matchMedia 的监听才是动态适配的关键点,这点容易被忽略。


















