使用 JavaScript 检测触摸设备
触摸检测听起来很简单,直到你面对 Surface Pro、配备妙控键盘的 iPad 或带触摸屏的 Chromebook。这些混合设备打破了”触摸”和”鼠标”互斥的假设——而这正是大多数检测方法失效的地方。
本文直击要点:什么方法真正有效,什么方法不行,以及什么时候根本不应该检测触摸。
核心要点
'ontouchstart' in window不可靠,因为浏览器暴露该属性的方式不一致,即使在没有触摸屏的设备上也可能存在。navigator.maxTouchPoints > 0是在现代浏览器中检测触摸能力最可靠的简单 JavaScript 方法。- CSS 指针媒体查询(
pointer、any-pointer、hover)无需任何 JavaScript 即可处理大多数 UI 适配。 - Pointer Events API 及其
pointerType属性让你能够检测用户当前的输入方式,这在混合设备上比页面加载时的一次性检测有用得多。
为什么 'ontouchstart' in window 还不够
多年来,开发者使用 'ontouchstart' in window 作为快速检测触摸能力的方法。问题在于浏览器暴露该属性的方式不一致。某些 Windows 8+ 上的桌面浏览器即使没有触摸屏也会将其报告为 true。Chrome 在不同版本中切换过其行为。这是一个不可靠的信号。
仅依赖 ontouchstart 意味着你在检测浏览器行为,而不是实际的硬件能力。
navigator.maxTouchPoints:更可靠的基准
当今检测触摸支持最可靠的 JavaScript 属性是 navigator.maxTouchPoints。它返回设备支持的最大同时触摸接触点数量。大于零的值表示硬件报告具有触摸能力。
function hasTouchSupport() {
return navigator.maxTouchPoints > 0
}
这是 Pointer Events 规范的一部分,在所有现代浏览器中都受支持——Chrome、Firefox、Safari 和 Edge。它简洁、可读,且不依赖于事件处理器嗅探。
注意:
navigator.msMaxTouchPoints是 IE 时代的等效属性。除非你在维护遗留代码,否则不需要它。
何时 CSS 指针媒体查询是更好的工具
对于大多数 UI 适配,你根本不需要 JavaScript。CSS 指针媒体查询让你能够根据主输入设备的精度调整样式。
/* 针对主指针为粗略型(如手指)的设备 */
@media (pointer: coarse) {
.btn {
min-height: 48px;
}
}
/* 针对任何可用的粗略型指针,包括辅助输入 */
@media (any-pointer: coarse) {
.tooltip {
display: none;
}
}
/* 针对悬停功能不可靠的设备 */
@media (hover: none) {
.dropdown:hover .menu {
display: none;
}
}
pointer 和 any-pointer 的区别在混合设备上很重要。pointer: coarse 仅反映主输入。any-pointer: coarse 在任何连接的输入为粗略型时返回 true——当设备同时具有鼠标和触摸屏时很有用。
Discover how at OpenReplay.com.
Pointer Events 与 Touch Events
Pointer Events 是处理鼠标、触摸和触控笔输入的现代统一模型。你无需维护独立的 touchstart 和 mousedown 处理器,只需编写一个:
element.addEventListener('pointerdown', (e) => {
if (e.pointerType === 'touch') {
// 处理触摸输入
} else if (e.pointerType === 'mouse') {
// 处理鼠标输入
}
})
pointerType 属性准确告诉你用户此刻在做什么——而不是他们的设备理论上能做什么。这是在混合设备上最重要的区别。
传统的 Touch Events(touchstart、touchmove、touchend)在大多数浏览器中仍受支持,但并非在所有环境中都可用,且不涵盖鼠标或触控笔输入。新代码应优先使用 Pointer Events。
混合设备问题
触摸笔记本电脑的用户可能在会话开始时使用鼠标,然后伸手触摸屏幕。你在页面加载时进行的任何检测都已经过时了。
与其锁定单一的检测结果,不如监听 pointerdown 事件并动态读取 e.pointerType。这让你能够根据用户实际在做什么来适配 UI,而不是根据他们的设备理论上支持什么。
何时使用什么
| 目标 | 推荐方法 |
|---|---|
| 调整按钮大小或触摸目标 | CSS @media (pointer: coarse) |
| 禁用仅悬停 UI | CSS @media (hover: none) |
| 在 JS 中检查触摸硬件能力 | navigator.maxTouchPoints > 0 |
| 处理所有类型的输入事件 | Pointer Events API |
| 动态检测当前输入类型 | pointerdown 上的 event.pointerType |
结论
完全跳过用户代理嗅探。对于 UI 调整,CSS 指针媒体查询无需一行 JavaScript 即可处理大多数情况。当你确实需要 JavaScript 时,navigator.maxTouchPoints 为你提供可靠的硬件信号,而 Pointer Events API 为你提供实时输入上下文。它们共同覆盖了所有现代设备——包括那些使简单检测变得不可靠的混合设备。
常见问题
是的。navigator.maxTouchPoints 是 Pointer Events 规范的一部分,在 Chrome、Firefox、Safari 和 Edge 中都受支持。在不报告触摸支持的设备上通常返回零,在支持触摸的设备上返回正整数。它是当今 JavaScript 中检测触摸支持最可靠的单属性检查。
用户代理字符串对于确定输入能力不可靠。它们标识浏览器和操作系统,而不是硬件。Windows 笔记本电脑和 Windows 平板电脑可能共享相同的用户代理字符串,尽管它们的输入方式截然不同。通过 maxTouchPoints 或 CSS 媒体查询进行特性检测要准确得多。
可以。Pointer Events API 在 pointerdown 等事件上提供 pointerType 属性。其值为 touch、mouse 或 pen,取决于当时使用的输入方式。这比一次性能力检查更有用,特别是在用户在输入方式之间切换的混合设备上。
pointer 媒体查询仅针对主输入设备。any-pointer 查询在任何可用输入匹配条件时返回 true。在同时具有触控板和触摸屏的笔记本电脑上,pointer: coarse 可能为 false,因为触控板是主输入,但 any-pointer: coarse 会是 true,因为触摸屏符合条件。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.