移动端300ms点击事件的延迟

移动端click事件300ms延迟

移动端click事件300ms的延迟在目前看来,已经是老生常谈了。

以下内容,我会在参考资源的基础上谈谈我对移动端click事件300ms延迟的一些理解。本人愚昧,如有不足,望浏览指正。

300ms延迟的原因

参考资源有谈到,300ms延迟是因为浏览器要判断用户的操作而规定的。浏览器不知道用户的手指离开屏幕之后是否会再次回到屏幕,还是就此结束触摸事件。为了确定用户接下来的操作,浏览器不得不等待一段时间。而浏览器开发者找到了一个最佳时间间隔,就是300ms。

而浏览器需要判断用户是否是双击屏幕以进行缩放页面的操作。如果两次click事件的时间间隔在300ms内,表示用户需要双击缩放页面;如果两次click事件的时间间隔在300ms外,表明用户只是需要触发click事件。

触摸事件

在移动端上,存在触摸(touch)事件、手势(gesture)事件和指针(pointer)事件。后两种事件由于使用的少和浏览器兼容性问题的原因,暂时不做讨论。

触摸事件包括touchstart, touchmove, touchend三个事件。

  • touchstart: 当手指触摸屏幕时触发,即使只有一只手指放在屏幕上也会触发
  • touchmove: 当手指在屏幕上连续滑动时触发,在这个事件期间,可以调用事件对象的preventDefault方法阻止页面滚动。
  • touchend: 当手指从屏幕上移开时触发。

以上三个事件都可以冒泡,同时可以被取消。事件对象包含touches数组,changedTouches数组,targetTouches数组,这三个数组包含着clientX/Y、pageX/Y、screenX/Y、target等属性。一般情况下在touchstart事件对象中读取touches数组获取相应的位置信息,在touchmove和touchend事件下读取changedTouches和targetTouches数组获取相应的位置信息。

click事件

前端工程师对click事件绝对不会陌生,click事件触发的事件处理程序是DOM和用户交互最直接的方式。

click事件是由mousedown事件和mouseup事件组成,同时mousedown和mouseup触发必须在同一个像素点上才会触发click事件。

即鼠标点击: mousedown -> mouseup -> click。如果是在按下鼠标的时候触发mousemove事件,将鼠标移动到其他像素点上,然后再触发mouseup事件,此时click事件是不会被触发的。

触摸事件与click事件

既然click事件在移动端上存在300ms的延迟,那我们直接使用触摸事件来代替click事件不就可以了吗?

这种做法是不行的。平时我们使用手机去浏览信息的时候,会不断的滑动页面。如果使用触摸事件替代click事件,那么会发生一些意想不到的事情。比如说,页面上有一个img标签,外层包含a标签,当点击时跳转到新页面。如果此时将某个事件处理程序绑定到touchstart事件时,在这个img标签上滑动屏幕,就会跳转到其他页面了。这并不是我们想要的结果。

再来看看另一个例子,我在documet对象上绑定了click事件,touchstart事件和touchend事件。通过各自事件的事件对象获取到点击时距离是视觉视口的X、Y距离。

document.addEventListener('click', e => {
  let touch = e
  console.log('click', touch.clientX, touch.clientY)
}, false)
document.addEventListener('touchstart', (e) => {
  let touch = e.touches[0]
  console.log('start', touch.clientX, touch.clientY)
}, false)
document.addEventListener('touchend', (e) => {
  let touch = e.changedTouches[0]
  console.log('end', touch.clientX, touch.clientY)
}, false)

当我在页面上触摸且不滑动时,会相继触发touchstart, touchend, click事件。此时会符合我们的预期。

当我在页面上触摸且滑动时,此时只会触发touchstart, touchend, 而不会触发click事件。原因很简单,因为在在触摸时发生了滑动,此时手指就不在一个像素点上了,所以不会触发click事件。

在移动端最理想的情况是,当用户滑动时不误触发某个事件处理程序,当页面静止时点击某个元素才会触发相应的操作,这是最好的情况。所以click事件在移动端仍然需要,不可丢弃。因此,就存在一些办法去处理移动端300ms延迟的问题。300ms延迟在移动端上的体验太差啦!受不了:)

300ms延迟解决方法

meta视口标签

既然300ms出现的原因是为了判断用户是否需要双击缩放页面的问题,那我们禁止🚫缩放就可以解决300ms延迟的问题了。这可以通过meta视口标签完成。

<meta name="viewport" content="width=device-width, maximum-scale=1, minimum-scale=1, user-scalable=no">

其中width=device-width可以使布局视口的宽度等于理想视口的宽度,因此也就禁止了页面的缩放。而maximum-scale=1, minimum-scale=1, user-scalable=no三个属性禁止了用户双指缩放页面。注意是双指缩放,与双击缩放不同。

对meta视口标签概念不熟悉的朋友,可以看看这篇文章。传送门:基于REM的移动端响应式适配方案

fastclick.js

fastclick.js 是FTLabs专门为解决移动端浏览器300毫秒点击延迟问题所开发的一个轻量级的库。简而言之,fastclick在检测到touchend事件的时候,会通过DOM自定义事件立即触发一个模拟click事件,并把浏览器在 300 毫秒之后真正触发的 click 事件阻止掉。

fastclick 的使用方法非常简单,在 window DOMContentLoaded 事件之后,在 <body>上调用fastclick.attach() 即可。

import fastclick from 'fastclick'
window.addEventListener('DOMContentLoaded', () => {
    fastclick.attach(document.body)
}, false)

attach() 方法虽可在更具体的元素上调用,直接绑定到 <body>上可以确保整个应用通过冒泡机制都能受益。当 fastclick 检测到当前页面使用了基于 <meta>标签或者 touch-action属性的解决方案时,会静默退出。可以说,这是真正的跨平台方案出来之前一种很好的变通方案。

指针事件和touch-action

指针事件和touch-cation的CSS方法都存在兼容性问题。由于并没有得到大多浏览器的支持,需要引入相应的polyfill才能实现,所以暂时不做讨论。

其中指针事件PC端和移动端浏览器兼容性如下

touch-action CSS属性PC端和移动端浏览器兼容性如下

最后

最后做一个总结。移动端click事件是必须的,300ms延迟主要用于判断用户是否是双击缩放操作。其中,解决300ms延迟的方法有

  1. 使用meta视口标签,解决双击缩放和双指缩放问题
  2. 引入fastclick.js,模拟click事件
  3. 指针事件和CSS touch-action属性(存在兼容性问题)

参考资源:

300 毫秒点击延迟的来龙去脉

移动端300ms点击延迟和点击穿透问题

posted @ 2017-08-04 22:41  凯斯keith  阅读(1191)  评论(0编辑  收藏  举报