关于移动端300ms延迟及解决方案

一般情况下,在移动开发过程中,针对click事件,浏览器会有300ms的派发延迟。由于这300ms的延迟,通常会带来一些问题,比如点击穿透。那么浏览器为什么有300ms的延迟呢?

300ms延迟的由来

这要追溯至 2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。
这当中最出名的,当属双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。
双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。 那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。
即:移动端要判断是否是双击,所以单击之后不能够立刻触发click,要等300ms,直到确认不是双击了才触发click。所以就导致了click有延迟。
鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。

但目前在开发webapp时,基本不需要双击缩放的功能,为了提高用户的使用体验,我们要解决这 300 ms延迟问题。以下为社区常见的几种解决方案:

浏览器解决方案

为了解决300ms延迟,浏览器的开发厂商,提供了一些解决方案。自2014年的Chrome 32版本已经把这个延迟去掉了,通过设置meta标签:

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

即把viewport设置成当前设备的实际像素,浏览器就没有300ms延迟。据官方说明,这个举动受到了IE/Firefox/Safari(IOS 9.3)的支持,可用来消除页面点击延迟。但对于低版本的浏览器,此方法并不适用。

另外针对chrome浏览器,通过设置initial-scale=1.0,也可以用来消除延迟,但safari无效。

1
<meta name="viewport" content="initial-scale=1.0">

fastclick

通过引入fastclick(FT Labs专门为解决移动端浏览器300ms点击延迟所开发的轻量级库)。
fastclick 通过检测手指点击时的touchstart事件 和 touchend 事件之间的移动距离来判断滚动还是点击,通过DOM自定义事件,立即触发一个模拟 click事件,并把浏览器在300ms之后真正触发的click事件阻止掉。

设置CSS

css 属性 touch-action 用于指定某个给定区域是否允许用户操作,以及如何响应用户操作。

manipulation:
Enable panning and pinch zoom gestures, but disable additional non-standard gestures such as double-tap to zoom. Disabling double-tap to zoom removes the need for browsers to delay the generation of click events when the user taps the screen. This is an alias for “pan-x pan-y pinch-zoom” (which, for compatibility, is itself still valid).

大意为:浏览器只允许进行滚动和持续缩放操作。任何其它被auto值支持的行为不被支持。启用平移和缩小缩放手势,但禁用其他非标准手势,例如双击以进行缩放。 禁用双击可缩放功能可减少浏览器在用户点击屏幕时延迟生成点击事件的需要。

通过设置:

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

只需上面简单的css设置,就解决了300ms的延迟问题,并且主要移动端Android Webview, Chrome for Android, Safria, 及采用webkit内核的浏览器均支持此属性。

综上最佳解决方案为利用CSS touch-action: manipulation属性。


关于点击穿透问题

移动端提供了 touchstart、touchmove、touchend,click事件等模拟鼠标事件。在移动端的触发顺序为: touchstart > touchmove > touchend > click。通过上述的事件触发顺序,我们就可以来说一下点击穿透问题了。

点击穿透的场景

页面中存在上下两层或多层,当上层的元素通过点击或触摸,导致DOM改变,下层中相对位置的元素触发了点击事件,这种现象就是点击穿透。

比较常见的是,页面有遮罩层,遮罩层上元素绑定touch事件,而遮罩层下方元素绑定了click事件,当touch事件触发后,遮罩层元素消失,浏览器300ms后触发该元素位置的click事件,由于遮罩层元素已经消失,就触发了当前位置元素的click事件。

参考