Back

使用 JavaScript 检测触摸设备

使用 JavaScript 检测触摸设备

触摸检测听起来很简单,直到你面对 Surface Pro、配备妙控键盘的 iPad 或带触摸屏的 Chromebook。这些混合设备打破了”触摸”和”鼠标”互斥的假设——而这正是大多数检测方法失效的地方。

本文直击要点:什么方法真正有效,什么方法不行,以及什么时候根本不应该检测触摸。

核心要点

  • 'ontouchstart' in window 不可靠,因为浏览器暴露该属性的方式不一致,即使在没有触摸屏的设备上也可能存在。
  • navigator.maxTouchPoints > 0 是在现代浏览器中检测触摸能力最可靠的简单 JavaScript 方法。
  • CSS 指针媒体查询(pointerany-pointerhover)无需任何 JavaScript 即可处理大多数 UI 适配。
  • Pointer Events API 及其 pointerType 属性让你能够检测用户当前的输入方式,这在混合设备上比页面加载时的一次性检测有用得多。

为什么 'ontouchstart' in window 还不够

多年来,开发者使用 'ontouchstart' in window 作为快速检测触摸能力的方法。问题在于浏览器暴露该属性的方式不一致。某些 Windows 8+ 上的桌面浏览器即使没有触摸屏也会将其报告为 true。Chrome 在不同版本中切换过其行为。这是一个不可靠的信号。

仅依赖 ontouchstart 意味着你在检测浏览器行为,而不是实际的硬件能力。

当今检测触摸支持最可靠的 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;
  }
}

pointerany-pointer 的区别在混合设备上很重要。pointer: coarse 仅反映输入。any-pointer: coarse任何连接的输入为粗略型时返回 true——当设备同时具有鼠标和触摸屏时很有用。

Pointer Events 与 Touch Events

Pointer Events 是处理鼠标、触摸和触控笔输入的现代统一模型。你无需维护独立的 touchstartmousedown 处理器,只需编写一个:

element.addEventListener('pointerdown', (e) => {
  if (e.pointerType === 'touch') {
    // 处理触摸输入
  } else if (e.pointerType === 'mouse') {
    // 处理鼠标输入
  }
})

pointerType 属性准确告诉你用户此刻在做什么——而不是他们的设备理论上能做什么。这是在混合设备上最重要的区别。

传统的 Touch Events(touchstarttouchmovetouchend)在大多数浏览器中仍受支持,但并非在所有环境中都可用,且不涵盖鼠标或触控笔输入。新代码应优先使用 Pointer Events。

混合设备问题

触摸笔记本电脑的用户可能在会话开始时使用鼠标,然后伸手触摸屏幕。你在页面加载时进行的任何检测都已经过时了。

与其锁定单一的检测结果,不如监听 pointerdown 事件并动态读取 e.pointerType。这让你能够根据用户实际在做什么来适配 UI,而不是根据他们的设备理论上支持什么。

何时使用什么

目标推荐方法
调整按钮大小或触摸目标CSS @media (pointer: coarse)
禁用仅悬停 UICSS @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.

OpenReplay