@media (hover: none) and (pointer: coarse) 是最可靠的触控设备判断组合,精准识别“当前主输入为手指触控且无悬停能力”的状态,支持主流浏览器,需连写无空格、不可省略 :coarse,应配合 JS 监听 pointerdown 和 matchMedia 变化实现动态适配。

@media (hover: none) and (pointer: coarse) 是目前最可靠的触控设备判断组合
这个组合能准确捕获“当前主输入为手指触控、且无悬停能力”的状态,Safari 14.1+、Chrome 51+、Firefox 62+ 全面支持。它不是在问“有没有触摸屏”,而是在问“此刻是否只能靠点按操作”——这才是交互适配的真实依据。
常见错误现象:单独用 @media (hover: hover) 判断桌面,结果 Surface Pro 合盖触控时仍命中;用 @media (max-width: 768px) 把带触控的 Windows 笔记本当手机,导致下拉菜单无法展开。
-
@media (hover: none) and (pointer: coarse)必须写成连写形式,中间不能有空格(如(hover: none)and(pointer: coarse)是合法的,但加空格后部分旧版 Safari 可能失效) - 不要漏掉
: coarse的值,@media (pointer)或@media (pointer: )会整个规则无效 - 该组合不匹配 iPad 连妙控板或鼠标后的状态——这恰恰是优点,说明它响应的是真实输入模式,而非硬件静态属性
@media (hover: hover) and (pointer: fine) 才该启用:hover样式
把所有依赖悬停的交互逻辑(比如 a:hover、.dropdown:hover .menu)严格限定在这个媒体查询内。它排除了所有纯触控场景,也过滤掉了 Surface 等设备在触控模式下的误报。
为什么不能只靠 @media (hover: hover)?因为 iPadOS 和 ChromeOS 只要设备“有能力悬停”(哪怕只是插过一次鼠标),就始终返回 hover: hover,和当前操作方式无关。
立即学习“前端免费学习笔记(深入)”;
-
@media (hover: hover) and (pointer: fine)中的pointer: fine是关键约束,确保只有鼠标、触控笔等高精度设备才触发 - Android WebView 4.4 及更早版本不支持
pointer,需 fallback 到@media (max-width: 768px)或 JS 检测(但仅作降级,不作主逻辑) - 别在该媒体查询里写过渡动画时长过短(如
transition: all .08s),鼠标响应快,但触控设备一旦误入此分支,动画会显得突兀
JavaScript 必须补足 CSS 的动态盲区
CSS 媒体查询本身不会监听“用户从鼠标切到手指”的瞬间。首次 pointerdown 且 event.pointerType === 'touch' 时,应主动标记触控模式;同时监听 window.matchMedia('(hover: none)').matches 变化,比轮询高效得多。
容易踩的坑:'ontouchstart' in window 完全不可靠——Chrome 桌面版、Linux 浏览器、甚至某些 Electron 应用都返回 true,但它和物理触摸能力毫无关系。
- 优先使用
window.matchMedia('(hover: none) and (pointer: coarse)')获取初始状态,而非navigator.maxTouchPoints(已废弃)或 UA 字符串 - 不要给
body加touch-modeclass 后再写一堆.touch-mode .tooltip样式——这会让 CSS 分支失控,且无法响应系统级指针切换 - 若需临时禁用某元素的 hover 行为,直接用
element.style.pointerEvents = 'none'比改 class 更轻量、更及时
粗粒度指针(pointer: coarse)不等于“是手机”
@media (pointer: coarse) 匹配的是输入精度,不是设备类型。Surface Pro、Chromebook、甚至某些游戏手柄都会触发它。单独使用会导致按钮尺寸在桌面触控屏上被无故放大,却在 iPad 连键盘后失效。
典型误用:用它做整页布局切换。正确做法是仅调整交互密度——比如把按钮 min-height 提到 48px,把 padding 加到 12px,移除纯 :hover 触发的二级菜单。
- 避免在
@media (pointer: coarse)下启用transition,手指操作的起止不明确,过渡动画易显卡顿 - 如果产品必须区分“纯触控”和“混合设备”,唯一可行路径是组合
(hover: none) and (pointer: coarse)+ JS 监听pointerdown+ 服务端 UA 辅助(仅用于首次加载兜底) - 旧 Android WebView 不支持
pointer特性,caniuse 显示兼容率约 94%,剩余 6% 场景建议用@media (max-width: 480px)作为视觉降级,而非功能降级
@media (hover: hover) and (pointer: fine) 就会立刻接管样式——你写的 CSS 必须能承受这种切换,而不是假设“触控模式永远固定”。


















