移动端点击事件延迟的诞生消亡史

快速反馈对于任何 UI 的实现都是至关重要的。研究表明,100ms 是界面让用户感到即时的最大延迟。尽管如此,移动网络仍然受到一个巨大的反馈问题的困扰:触摸任何元素后,延迟 300 毫秒。这种延迟是许多用户认为基于 HTML 的 Web 应用程序“卡顿”的最重要原因之一。本文将带你了解移动端点击事件延迟的从诞生到消亡的过程。

诞生史

在 2007 年,苹果公司发布首款 iPhone 之前,由于当时的网站普遍为大屏幕设备所设计,为了应对 iPhone 这种小屏幕设备浏览桌面网站的问题,由此,苹果引入了多项变革,其中就包含了“双击缩放准确定位正文主体,并将其缩放至适合的比例展现”的功能,即双击缩放功能。然而,由于这种双击缩放的操作,在用户第一次单击页面元素时,浏览器并不知道用户是想做双击缩放操作还是普通的单击操作。因此,IOS Safari 浏览器首先引入了 300 毫秒延迟,用来判断用户是否会再次点击,也就是说,在第一次点击延迟 300 毫秒,300 毫秒后用户没有再次点击则认定为用户在进行普通的单击操作,并触发单击(Click)事件。

全面的移动开发者与单击事件延迟战争拉开了序幕。鉴于 iPhone 的巨大成功,其他浏览器厂商也都快步跟进纷纷效仿了 iPhone Safari 浏览器的做法。于是,单击事件延迟成为了移动开发者不得不面对的痛。

消亡史

虽然从当时来看 300ms 延迟并没有什么不妥,然而在越来越注重用户体验的移动互联网时代,这种延迟是无法被用户所接受的,加之开发者也可以对网站进行响应式适配,双击缩放的操作变成了一种可有可无的操作,浏览器厂商开始意识到延迟所带来的体验问题,提出了一些解决方案。

禁用缩放

解决此问题的第一个方法是常识性方法。由于延迟产生的原因是双击缩放操作,那么就禁用页面缩放功能,最直接的方法就是设置 Viewport 禁止缩放,代码如下:

1
2
3
<meta name="viewport" content="user-scalable=no">
<!-- 或者 -->
<meta name="viewport" content="initial-scale=1,maximum-scale=1">

适用于 Android 的 Chrome 浏览器是第一个引入此更改的应用程序,随后紧接着是 Android 的 Firefox。没有其他浏览器供应商宣布要添加此优化的计划。尽管此解决方案非常巧妙,背后却以牺牲整个页面缩放为代价,带来的影响是对于页面上的图像或小文本,想要进行缩放变得难以完成。即大多数网站都无法从 Android 版 Chrome 和 Android 版 Firefox 的优化中受益。

幸运的是,随后 Chrome 团队在 Chrome 32 之后的版本中,提出了新的优秀方案,代码如下:

1
<meta name="viewport" content="width=device-width">

约定当 Viewport 的 width 小于或等于 device-width 时,去除双击缩放功能。这项技术的另一个关键在于,它仅消除了双击缩放的功能,用户依然可以使用双指缩放功能。因此,不存在与禁用缩放相关的可用性和可访问性问题。

指针事件

指针事件是 Microsoft 提出的一系列针对 Web 的新事件,现已成为 W3C 规范。指针事件规范是尝试使用单个事件模型统一我们对所有输入类型(鼠标,触摸,手写笔等)的处理。根据规范,CSS 属性 touch-action 用于设置触摸屏用户如何操纵元素的区域(例如,浏览器内置的缩放功能)。touch-action 默认值是 auto,当设置为 none 时会禁止用户缩放,能成功解决 300ms 延迟的问题,如:

1
2
3
4
a[href],
button {
touch-action: none;
}

甚至可以添加 touch-action: none 到 body 以完全禁用双击来缩放(注意:这也将禁用双指缩放功能,因此它与我们前面讨论的与禁用缩放相关的可访问性和可用性问题相同)。

2014年3月13日,W3C 规范增添了新的 touch-action 属性值 manipulation。该属性值提供了两全其美的体验;它允许双指缩放,以避免 touch-action: none 出现的可访问性和可用性问题,但它仍然可以通过禁用双击缩放来消除 300ms 的延迟。

FastClick

FastClick 是一个小型 JavaScript 库,专门旨在防止移动浏览器中的 300ms 点击延迟。FastClick 的实现基础建立于 touchstart ,touchmove 或者 touchend 事件中的任意一个调用 event.preventDefault,mouse 事件 以及 click 事件将不会触发。FastClick 的原理在 touchend 阶段调用 event.preventDefault,然后通过 document.createEvent 创建一个自定义事件 MouseEvents,然后通过 event​Target​.dispatch​Event 触发对应目标元素上绑定的 click 事件。

关于 FastClick 的好处是,它非常容易使用,只需在文档加载后调用 FastClick.attach() 在 body 元素上实例化:

1
2
3
4
5
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}

FastClick足够聪明,可以检测到如果是桌面浏览器或者存在 meta 标记和 touch-action 解决方案的时候,不会执行任何操作。因此,在我们为所有平台提供真正的解决方案之前,这是一个极好的解决方法。

现代浏览器

得益于现代浏览器对 W3C 规范 touch-action: manipulation 的支持性,才真正彻底解决了点击事件延迟的问题。touch-action: manipulation 规定浏览器只允许进行滚动和持续缩放操作。任何其它被 touch-action: auto 支持的行为不被支持。启用平移和双指缩放手势,但禁用其他非标准手势,例如双击缩放。 禁用双击缩放功能可减少浏览器在用户点击屏幕时延迟生成点击事件的需要。代码如下:

1
2
3
html {
touch-action: manipulation;
}

从此,移动端点击事件延迟正式宣告消亡。