navigator.maxTouchPoints 比 UA 更可靠,因其直接反映硬件多点触摸能力,不依赖字符串匹配、不可篡改、不受模拟器或UA覆盖影响,且能准确识别Surface等支持触摸的现代桌面设备。
navigator.maxTouchPoints 是浏览器原生暴露的设备能力指标,直接反映硬件是否支持多点触摸。它不依赖字符串匹配、不被用户可篡改、也不受模拟器或开发者工具 UA 覆盖影响。现代桌面设备(如 Surface、MacBook Touch Bar)也可能返回 >0,这反而是优势——说明你检测的是「交互能力」而非「设备类型」,正好契合 UI 降级的真实需求:不是“是不是手机”,而是“能不能用手指滑动”。注意:maxTouchPoints 在部分旧版 Safari(iOS undefined;Chrome 84+、Firefox 65+、Edge 79+ 支持良好。它不是布尔值,而是一个数字,典型值为 0(无触摸)、5(常见手机)、10(高端平板)。
推荐做法是结合 CSS 媒体查询与 JS 运行时判断:
@media (hover: hover) and (pointer: fine) 控制 hover 样式,但仅作视觉补充,不作为交互依据navigator.maxTouchPoints > 0 为真值,触发以下操作:mouseenter/mouseleave 监听器click 事件替换为 touchstart(并加 { passive: false } 防止默认滚动拦截失效)scroll-behavior: smooth 时需确认是否在触摸滚动中造成卡顿,必要时回退为 auto
示例代码片段:
const isTouchDevice = "maxTouchPoints" in navigator && navigator.maxTouchPoints > 0;if (isTouchDevice) { document.body.classList.add("touch-mode"); // 禁用 hover 驱动的下拉菜单 document.querySelectorAll("[data-hover-dropdown]").forEach(el => { el.removeAttribute("data-hover-dropdown"); });}
maxTouchPoints === 0 只代表当前环境不报告触摸能力,不等于没有触摸屏——比如 Windows 上 Chrome 开启了「禁用触摸」标志、某些企业定制 ROM 屏蔽了该 API、或用户在远程桌面中操作。此时应启动回退策略:
"ontouchstart" in window(兼容性更好,但可能被 polyfill 干扰)touchstart 事件,用 once: true 避免污染matchMedia("(pointer: coarse)").matches,这个媒体查询在 Safari 11.1+ 和主流浏览器中稳定可用不要只靠单一条件做最终判定。尤其在金融、政务类应用中,误判会导致关键按钮无法点击。
maxTouchPoints 在折叠态/展开态下通常保持不变(如 Mate X5 展开后仍返回 10),但它无法告诉你当前屏幕形态是否适合展示侧边栏或分栏布局。这时候你需要组合使用:window.matchMedia("(min-width: 768px)").matches 判断视口宽度window.visualViewport?.width 获取真实可视区域(防缩放干扰)screen.orientation 变化,但注意该 API 在桌面端不可用真正复杂的不是检测,而是 UI 降级后的状态同步:比如用户在折叠态下收起导航,再展开时要不要自动恢复?这类细节不会被 maxTouchPoints 告诉你,得靠业务逻辑兜底。