解决click事件在移动端操作延迟300ms问题和点击时穿透问题

Posted JackieDYH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决click事件在移动端操作延迟300ms问题和点击时穿透问题相关的知识,希望对你有一定的参考价值。

为什么会有300ms延迟

在正常情况下,如果不进行特殊处理,移动端在触发点击事件时,会有300ms的延迟。换句话说,当我们在点击移动端页面后不会立即做出反应,而是会等待300ms才会触发click事件。在移动web兴起初期,用户对300ms的延迟没有太大的感觉,但随着用户对交互体验的要求的提高,如今,移动端的300ms延迟严重影响了用户的使用体验

07年,苹果公司发布首款Iphone前夕,遇到一个问题:当时的网站都是为大屏设计,手机屏幕太小无法正常浏览,于是苹果工程师做了一些约定解决此类问题。
这些约定当中,最为有名的是双击缩放(double tap to zoom),这是产生300ms延迟的根源。
用手指在屏幕上快速点击两次,ios 自带的 Safari 浏览器会将网页缩放至原始比例。如果用户在 iOS Safari
里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。

因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。 由此产生了300ms延迟问题

解决

1、fastclick插件

FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。

安装

npm install fastclick -S

引入

import FastClick from 'fastclick'

使用

FastClick.attach(document.body);

页面中直接引入fastclick.js

<script type='application/javascript' src='./fastclick.js'></script>

初始化实例

//javascript版本
if ('addEventListener' in document) {
	document.addEventListener('DOMContentLoaded', function() {
		FastClick.attach(document.body);
	}, false);
}

//jQuery版本
$(function() {
	FastClick.attach(document.body);
});

//Common JS模块的系统方式
var attachFastClick = require('fastclick');
attachFastClick(document.body);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            width: 50px;
            height: 50px;
            background-color: pink;
        }
    </style>
    <script src="./fastclick.js"></script>
</head>

<body>
    <div></div>
    <script>
         if ('addEventListener' in document) {
             document.addEventListener('DOMContentLoaded', function () {
                 FastClick.attach(document.body);
             }, false);//不冒泡
         }
        var div = document.querySelector('div');
        div.addEventListener('click',function(){
            alert(111);
        })
    </script>
</body>

</html>

点击穿透

说完移动端点击300ms延迟的问题,还不得不提一下移动端点击穿透的问题。可能有人会想,既然click点击有300ms的延迟,那对于触摸屏,我们直接监听touchstart事件不就好了吗?
使用touchstart去代替click事件有两个不好的地方。
第一:touchstart是手指触摸屏幕就触发,有时候用户只是想滑动屏幕,却触发了touchstart事件,这不是我们想要的结果;
第二:使用touchstart事件在某些场景下可能会出现点击穿透的现象。

什么是点击穿透?
假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。

这是因为在移动端浏览器,事件执行的顺序是touchstart > touchend >
click。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。


2、禁用页面缩放

在html文档头部的meta标签中加入如下语句

<!-- 1.禁用缩放 user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">

user-scalable=no表明这个页面不可缩放,也就是浏览器禁用的双击缩放事件并且同时会去掉300ms点击延迟
但这个方案也有缺点,就是完全禁用了双击缩放,当我们需要放大文字或者图片时无法满足我们的需求。


3、封装一个处理函数

//封装tap解决click 300ms 延时
function tap (obj,callback) {
	var isMove = false;//记录手指是否移动
	var startTime = 0;//记录手指触摸的时间
	obj.addEventListerner('touchstart',function(e){
		startTime = Date.now();//记录触摸时间
	})
	obj.addEventListerner('touchmove',function(e){
		isMove = true;//查看手指是否滑动
	})
	obj.addEventListerner('touchend',function(e){
		if(!isMove && (Date.now()-statrTime) < 150){
		callback && callback();
		}
		isMove = false;//取反 重置
		startTime = 0;
	})
};
tap(div,function(){ //执行代码 });

这个代码可以监测元素点击发生时的状态,从而避免300ms的延迟。
但这个方法有一个弊端,一次只能给一个元素去解决问题。

 

以上是关于解决click事件在移动端操作延迟300ms问题和点击时穿透问题的主要内容,如果未能解决你的问题,请参考以下文章

移动端延迟300ms的原因以及解决方案

web移动端安卓和ios的一些兼容问题

移动端click事件延时

使用原生JS封装Tap事件,解决移动端300ms延迟

fastclick

300ms点击延迟