一、前言

移动端浏览器提供一个特殊的功能:双击(double tap)缩放。

 

二、移动端延迟300ms的原因

为什么要用触摸事件?触摸事件是移动端浏览器特有的html5事件。

因为移动端的click有很大延迟(大约300ms),300ms延迟来自判断双击和长按,因为只有默认等待时间结束以确定没有后续动作发生时,才会触发click事件。而触摸事件的延迟则是非常短的,使用触摸事件的能够提高页面响应速度,带来更好的用户体验。

重点:由于移动端会有双击缩放的这个操作,因此浏览器在click之后要等待300ms,看用户有没有下一次点击,也就是这次操作是不是双击。

三、浏览器开发商的解决方案

1、方案一:禁用缩放

当HTML文档头部包含如下meta标签时:

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

表明这个页面是不可缩放的,那双击缩放的功能就没有意义了,此时浏览器可以禁用默认的双击缩放行为并且去掉300ms的点击延迟

缺点:就是必须通过完全禁用缩放来达到去掉点击延迟的目的,然而完全禁用缩放并不是我们的初衷,我们只是想禁掉默认的双击缩放行为,这样就不用等待300ms来判断当前操作是否是双击。但是通常情况下,我们还是希望页面能通过双指缩放来进行缩放操作,比如放大一张图片,放大一段很小的文字。

2、方案二:更改默认的视口窗口

为了让桌面站点能在移动端浏览器正常显示,移动端浏览器默认的视口宽度!=设备浏览器视窗宽度,而是视口宽度要比设备宽度大,通常是980px。

我们可以通过以下标签来设置视口宽度设备宽度

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

对移动端坐过适配和优化了,这个时候就不需要双击缩放了。如果能够识别出一个网站是响应式的网站,那么移动端浏览器就可以自动禁掉默认的双击缩放行为并且去掉300ms的点击延迟。如果设置了上述meta标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。

这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面

方案三:css 的 touch-action

除了IE之外的大部分浏览器都不支持这个新的CSS属性。touch-action这个CSS属性。这个属性指定了相应元素上能够触发的用户代理(也就是浏览器)的默认行为。如果将该属性值设置为touch-action: none,那么表示在该元素上的操作不会触发用户代理的任何默认行为,就无需进行300ms的延迟判断

四、代码解决方案

1、方案一:指针事件polyfill

除了IE,其他大部分浏览器都还不支持指针事件。有一些JS库,可以让我们提前使用指针事件。比如:

(1)谷歌的Polymer

(2)微软的HandJS

(3)@Rich-Harris 的 Points

关心的不是指针事件,而是与300ms延迟相关的CSS属性touch-action。由于除了IE之外的大部分浏览器都不支持这个新的CSS属性,所以这些指针事件的polyfill必须通过某种方式去模拟支持这个属性。一种方案是JS去请求解析所有的样式表,另一种方案是将touch-action作为html标签的属性。

 

2、方案二:FastClick

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

五、点击穿透问题

说完移动端点击300ms延迟的问题,还不得不提一下移动端点击穿透的问题。既然click点击有300ms的延迟,那对于触摸屏,我们直接监听touchstart事件不就好了吗?

使用touchstart去代替click事件有两个不好的地方。

第一:touchstart是手指触摸屏幕就触发,有时候用户只是想滑动屏幕,却触发了touchstart事件,这不是我们想要的结果;

第二:使用touchstart事件在某些场景下可能会出现点击穿透的现象。

1、什么是点击穿透?

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

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

2、点击穿透现象3种情况

(1)点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件。

(2)跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转因为 a标签跳转默认是click事件触发 ,所以原理和上面的完全相同

(3)点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了。

3、解决方案

2种思路:

(1)不要混用touch和click。既然touch之后300ms会触发click,只用touch或者只用click就自然不会存在问题了。

(2)用掉(或者说是消费掉)touch之后的click。依旧用tap,只是在可能发生点击穿透的情形做额外的处理,拿个东西来挡住、或者tap后延迟350毫秒再隐藏mask、pointer-events、在下面元素的事件处理器里做检测(配合全局flag)

详细方案:

(1)只用touch

最简单的解决方案,完美解决点击穿透问题。

把页面内所有click全部换成touch事件 touchstart 、’touchend’、’tap’, 需要特别注意 a标签,a标签的href也是click,需要去掉换成js控制的跳转,或者直接改成span + tap控制跳转。

(2)只用click

下下策 ,因为会带来300ms延迟,页面内任何一个自定义交互都将增加300毫秒延迟,想想都慢。不用touch就不会存在touch之后300ms触发click的问题。

(3)拿个东西挡住

比较笨的方法, 千万不要用。更多信息请查看 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

(4)tap后延迟350ms再隐藏mask

改动最小,缺点是隐藏mask变慢了,350ms还是能感觉到慢的。

(5)pointer-events

比较麻烦且有缺陷, 不建议使用。mask隐藏后,给按钮下面元素添上 pointer-events: none; 样式,让click穿过去,350ms后去掉这个样式,恢复响应。缺陷是mask消失后的的350ms内,用户可以看到按钮下面的元素点着没反应,如果用户手速很快的话一定会发现。

(6)在下面元素的事件处理器里做检测(配合全局flag)

比较麻烦, 不建议使用。全局flag记录按钮点击的位置(坐标点),在下面元素的事件处理器里判断event的坐标点,如果相同则是那个可恶的click,拒绝响应。

(7)fastclick

好用的解决方案,不介意多加载几KB的话, 不建议使用 ,因为有人遇到了bug,更多信息请查看: Fastclick 导致click事件触发两次的问题

首先引入fastclick库,再把页面内所有touch事件都换成click,其实稍微有点麻烦,建议引入这几KB就为了解决点透问题不值得,不如用第一种方法呢。

六、浏览器事件触发的顺序

touchstart --> mouseover(有的浏览器没有实现) --> mousemove(一次) -->mousedown --> mouseup --> click -->touchend

Touch 事件中,常用的为 touchstart, touchmove, touchend 三种。除此之外还有touchcancel。 注意,原生事件中并没有tap事件。

事件描述如下:

事件 描述 触发时机
touchstart 开始触摸 手指接触屏幕时立即触发
touchmove 移动或拖拽 取决于系统和浏览器
touchend 触摸结束 手指离开屏幕时立即出发

而Touch事件的触发一般通过手指,还会存在多点触控,拖拽方向等情况。列出几个重要参数如下:

参数 含义
touches 屏幕中每根手指信息列表
targetTouches 和touches类似,把同一节点的手指信息过滤掉
changedTouches 响应当前事件的每根手指的信息列表

代码获取如下:

elemenrRef.addEventListener('touchstart', function(e) {
console.log(e.touches, e.targetTouches, e.changedTouches);}
);

手指触发触摸事件的过程如下:

touchstart --> mouseover(有的浏览器没有实现) --> mousemove(一次) -->mousedown --> mouseup --> click -->touchend

由此,我们可以在 ontouchstart 事件上记录开始触摸开始,ontouchend 记录触摸结束信息。 通过上述这些参数,很容易的去计算幽冥点击的时间,以及点击穿透的相关信息,包括响应的坐标情况

【注:我是saucxs,也叫songEagle,松宝写代码,文章首发于sau交流学习社区 https://www.mwcxs.top),关注我们每天阅读更多精彩内容】

设计方案--移动端延迟300ms的原因以及解决方案的更多相关文章

  1. 移动端click事件延迟300ms的原因以及解决办法

    这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题 —— 当时的网站都是为大屏幕设备所设计的.于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点 ...

  2. 移动端click事件延迟300ms的原因以及解决办法[转载]

    原文:http://www.bubuko.com/infodetail-822565.html 这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题 —— 当时的网站都是为 ...

  3. 苹果浏览器移动端click事件延迟300ms的原因以及解决办法

    这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题 —— 当时的网站都是为大屏幕设备所设计的.于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点 ...

  4. 移动端click事件延迟300ms问题

    因为历史原因,移动端点击事件会有300ms延迟,来判断用户是连续双击缩放还是点击跳转.即如果300ms内连续点击两次,则会理解为对页面进行缩放操作(当然前提是移动端页面设置为可缩放的):在一次点击之后 ...

  5. 移动端click事件延迟300ms到底是怎么回事,该如何解决?

    不管在移动端还是PC端,我们都需要处理用户点击,这个最常用的事件.但在touch端click事件响应速度会比较慢,在较老的手机设备上会更为明显(300ms的延迟). 问题由来 这要追溯至 2007 年 ...

  6. zepto的touch模块解决click延迟300ms问题以及点透问题的详解

    大家都知道移动端的click事件会延迟300ms触发,这时大家可以使用zepto的touch模块,里面定义了一个tap事件,通过绑定tap事件,可以实现点击立即触发的功能. 那么,它的tap事件是怎么 ...

  7. mysql主从延迟高的原因

    1.1.1故障1:从库数据与主库冲突 1 2 3 4 5 6 show slave status; 报错:且show slave status\G Slave_I/O_Running:Yes Slav ...

  8. (转)MySQL出现同步延迟有哪些原因?如何解决?

    http://oldboy.blog.51cto.com/2561410/1682147----MySQL出现同步延迟有哪些原因?如何解决? 原文:http://www.zjian.me/mysql/ ...

  9. 不能绑定到端口号:9194原因:Cannot assign requested address: JVM_Bind

    晚上将老服务器程序从win2008部署在新的云服务器win2012上,其实就是复制过去改改配置,启动时突然报不能绑定到端口号:9194原因:Cannot assign requested addres ...

随机推荐

  1. 使用了eclipse10年之后,我终于投向了IDEA

    使用了eclipse10年之后,我终于投向了IDEA 最近,改用了idea,同事都说我投敌了.当然,这些同事都是和我一样的"老"程序员.不说毕业生,公司里的90后基本电脑都不会安装 ...

  2. django 网站项目测试

    视图和 URL 配置: 在先前创建的 meishiweb目录下的 meishiweb 目录新建一个 view.py 文件,并输入代码: 此时在浏览器即可访问: 证明已经成功 我们也可以修改成以下的规则 ...

  3. magento 用程序生成优惠劵码

    参考自http://fragmentedthought.com/fragments/programatically-creating-sales-rule-coupon-code 上面的代码只能生成C ...

  4. 转-MySQL for Mac 安装和基本操作

    一.安装mysql 1.mysql下载地址:http://dev.mysql.com/downloads/mysql/ 2.安装软件包位于硬盘映象(.dmg)文件中,必须首先双击搜索起中的图标来安装该 ...

  5. div里面整齐的字体样式,所有浏览器都兼容

    <div id="wenda"> <div class="table_wd" > <div class="tr1&quo ...

  6. Python学习 Day 1-简介 安装 Hello world

    简介 Python(英语发音:/ˈpaɪθən/), 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年,Python 源 ...

  7. Activity的四种启动模式区别

    (1) standard 模式启动模式,每次激活Activity时都会创建Activity,并放入任务栈中. (2) singleTop 如果在任务的栈顶正好存在该Activity的实例, 就重用该实 ...

  8. vue2.0 动态切换组件

    组件标签是Vue框架自定义的标签,它的用途就是可以动态绑定我们的组件,根据数据的不同更换不同的组件. <!DOCTYPE html> <html lang="en" ...

  9. [转] 以超级管理员身份运行bat

    (转自:以超级管理员身份运行bat - lishirong   原文日期:2013.07.04) 废话不多说,直接上代码: -------------------------------------- ...

  10. 今天被 <!doctype html> 搞了两个小时,两个页面同样的样式,chosen右边的小箭头,一个上下居中对齐 一个居顶对齐。最后找到问题所在doctype

    今天被 <!doctype html> 搞了两个小时,两个页面同样的样式,chosen右边的小箭头,一个上下居中对齐 一个居顶对齐.最后找到问题所在doctype <-- 这个小箭头