移动端的穿透问题

穿透现象与click的延迟有着很重要的联系,刚开始出现这个问题也是让我疑惑了半天,想解决这个问题要先了解click延迟的解决原理。

移动端click事件300ms延迟的来源:

在网上简单了解了一下 click 事件300ms延迟的来源,这要追溯至 2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。这当中最出名的,当属双击缩放(double tap to zoom)。这也是会有上述 300 毫秒延迟的主要原因。为了实现触屏中双击放大效果,当用户点击屏幕时后会判断在300ms内是否有第二次点击,如果有,就理解成双击,若没有就是单击, 就会触发click事件. 当你点击移动设备的屏幕时, 可以分解成多个事件,顺序依次为:touchstart — touchmove — touchend — click, 这些事件是按顺序依次触发的 。鉴于 iPhone 的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放。几乎现在所有的移动端浏览器都有这个功能。 六年前,一个人们还在为通过移动设备上网而惊叹的时期,如此性能损失并无大碍。然而如今,是个移动端开发的 web 应用性能可以同原生应用匹敌的时代,所有的单击事件都有 300 毫秒延迟,必然是不可接受的。此外,随着响应式设计的逐步推进,开发者们已经根据设备本身的尺寸对站点进行了优化,也就逐渐淘汰了诸如双击缩放的约定。

解决click事件300ms延迟的办法:

移动端touch事件是没有延迟的,可以使用touchend来替代click事件来进行触发。

zepto 中 tap 事件的原理:

自定义tap事件,当用户点击元素时,touchend事件先发生, 当touchend事件冒泡到document时触发目标元素绑定的tap事件,先来简单的模拟一下 tap 事件(这里忽略 touchstart 和 touchend 之间的计算)

1
2
3
element.addEventListener("touchend",function (event) {
console.log("触发了touchend事件");
},false);

举个例子,有一个遮罩层中带有一个button用来关闭当前遮罩层,触发时遮罩层消失,而在该遮罩层下方正好有一个 a 标签链接,此时点击遮罩层上面的关闭 button,同时也会触发下层元素的 a 标签链接跳转,出现所谓穿透的现象。

出现穿透的原因:

当触发 tap 事件时,上层遮罩层关闭后,此时事件只进行到touchend,而click是在大概300ms后才触发,当click触发时,上面的遮罩层已经关闭,所以这时候相当于点击到了下面的 a 标签链接。

下层什么样的元素才会形成穿透:

根据原理来说,因为穿透是发生在click发生时,也就是下层绑定了click事件或click时会触发的事件(focus focusout)的元素,或点击时有默认形为的标签元素,如a标签、 input标签等。

解决穿透的办法:

  1. 直接将上层元素的tap事件换成click事件(会出现300ms的延迟触发事件)
  2. 在click事件触发前阻止它,如在touchend的事件中使用e.preventDefault()来阻止后续的click事件

zepto为何不使用e.preventDefault()来解决穿透问题?

我在网上看到了这样的解释:因为zepto的tap事件统一是在document的touchend时触发的,若在这里使用e.preventDefault(),那页面上所有元素在touchend后触发的事件都不会被执行了。

刚刚有提到事件冒泡,再来简单说一说事件冒泡和事件捕获。

在js高设中是这样定义是事件冒泡和事件捕获的:

  1. 事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐级想上传播到较为不具体的节点(文档);
  2. DOM事件流规定的事件流包括三个阶段: 事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
  3. 其它坑: input 的 change 事件在PC端中取消选择不会触发,在移动端不论选不选择都会触发 change 事件,如果是file类型,获取到的文件name 属性值是 / ,这一点需要注意。
作者

晓策

发布于

2017-08-31

更新于

2022-06-09

许可协议

评论