移动端click事件的延迟处理

前言

For many years, mobile browsers applied a 300-350ms delay between touchend and click while they waited to see if this was going to be a double-tap or not, since double-tap was a gesture to zoom into text.

一直以来移动端在touchendclick事件的触发上都有着300到350ms的间隔,用以区分用户是否执行的是双击动作,这就使得如果使用点击事件会在移动端造成体验上的问题。

Google的文档对此也有介绍 — 300ms tap delay gone away,在2014年推出的chrome 32版本中引入一种移除此延迟的方式,即添加如下的meta标签:

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

不久之后IE/Edge/Firefox/iOS 9.3也都开始支持这一特性。

同时你也可以通过css来实现(原文提到火狐不支持,但2017年的今天是已经支持了的):

html {
  touch-action: manipulation;
}

tap事件及引申

其实 zeptofastclick 这样的库已经提供了 tap 事件的实现,但是我们自己也可以尝试来实现一下。

zepto 提供的自定义的 tap 事件就是一个没有延迟的 click 事件,而 fastclick 是在touchend 之后生成一个 click 事件,并且立即触发它,再取消原本的 click 事件。这两者的原理都是在 touchend 之后触发,区别只在于触发的是原生的还是自定义的事件。

其中问题的关键在于,用户的手指接触到屏幕以后,可以是单击双击,也可以是滑动,因此不能够在每次的 touchend 事件之后都直接触发 tap。那么如何判定用户在点击还是滑动呢,zepto 是用的位移偏差,即记录下 touchstart 的时候的初始位移,然后用 touchend 的时候的位移减掉初始位移的偏差,如果这个差值在某一个固定值以内,则认为用户是点击,大于则认为是滑动。而 fastclick 是用的时间偏差,分别记录 touchstarttouchend 的时间戳,如果它们的时间差大于某一固定值,则认为是滑动操作,否则是点击操作。

同样的引申开来,如果要自己去做一个放大或者缩小的手势,我们也可以在 touchstart 里面获取 event.touches 两根手指的初始位置,保存初始化手指的距离,然后在 touchmove 里面再次获取新位置,计算新的距离减掉老的距离,如果是正数则说明是放大,反之缩小,放大和缩小的尺度也是可以取到一个相对值。