navigator.maxtouchpoints 是判断设备真实触控能力的可靠指标,值为0表示未报告触摸能力而非无触控屏,需结合媒体查询和事件监听综合判断交互模式。

直接用 navigator.maxTouchPoints 判断设备是否支持触控,比看 UA 字符串靠谱得多——它不骗人、不模拟、不依赖字符串匹配,只反馈硬件真实的多点触摸能力。但要注意:它不是“是不是手机”的开关,而是“能不能用手指操作”的信号。
用 maxTouchPoints 做第一层能力判断
这个值是数字,不是布尔值。典型情况如下:
- 0:当前环境未报告触摸能力(可能是无触摸屏,也可能是被禁用、远程桌面、定制 ROM 屏蔽等)
- 1–5:常见手机或入门平板(如 iPhone、中端 Android)
- 10+:高端平板或二合一设备(如 Surface Pro、iPad Pro)
检测写法要带存在性检查,避免旧 Safari 报错:
const isTouchCapable = "maxTouchPoints" in navigator && navigator.maxTouchPoints > 0;
结合媒体查询识别真实交互倾向
有触摸能力 ≠ 用户正在用手指操作。比如 Surface 插着鼠标时,maxTouchPoints > 0 仍为真,但用户实际在用精细指针。这时要用 CSS 媒体查询补全上下文:
-
(hover: none) and (pointer: coarse)→ 基本可认定是手持触控场景(手指为主) -
(hover: hover) and (pointer: fine)→ 鼠标/触控笔主导,适合 hover + click
监听变化,动态响应:
const mediaQuery = window.matchMedia('(hover: none) and (pointer: coarse)');<br>mediaQuery.addEventListener('change', e => {<br> if (e.matches) {<br> document.body.classList.add('touch-active');<br> } else {<br> document.body.classList.remove('touch-active');<br> }<br>});
UI 降级不是换 class,而是迁移交互逻辑
真正要改的是行为,不是样式。重点落在三件事上:
- 移除所有
mouseenter/mouseleave监听器(触摸设备无悬停概念) - 把关键
click替换为touchstart,并加{ passive: false }防止滚动被拦截失效 - 隐藏或禁用依赖鼠标的控件:右键菜单、拖拽排序、hover 下拉菜单等
示例逻辑:
if (isTouchCapable && mediaQuery.matches) {<br> document.body.classList.add('touch-mode');<br> document.querySelectorAll('[data-hover-dropdown]').forEach(el => {<br> el.removeAttribute('data-hover-dropdown');<br> });<br>}
别把 maxTouchPoints === 0 当成“一定是 PC”
这是最常踩的坑。值为 0 只说明“没报告触摸能力”,不代表设备没有触摸屏。可能原因包括:
- Chrome 启用了
--disable-touch-events标志 - 企业内网浏览器屏蔽了该 API
- 用户在远程桌面或虚拟机中操作
此时应启动回退策略:
- 查
"ontouchstart" in window(兼容性更好,但注意 polyfill 干扰) - 监听一次
touchstart事件(首次触发即确认) - 最终 fallback 到媒体查询
(pointer: coarse)单独判断










