在移动端click事件会有300ms的延迟, 用肉眼可以明显感觉到, 因此在开发中常常会遇到点击穿透的问题, 令不少初学者头疼,本篇文章将从由来, 常见问题, 解决方案几个方面来阐述点击穿透现象, 给予初学者以参考.

一. 300ms延迟的由来

这要追溯至2007年初, 苹果公司在发布首款iphone手机时, 大多数网站都是为大屏幕适配的,在手机上浏览桌面端页面体验着实不好. 考虑到用户体验, 苹果的工程师们做了一些约定, 为增强小屏幕浏览桌面端站点的用户体验, 提出了”双击缩放“的标准. “双击缩放”(double tap to zoom)即点击第一次后等待300ms, 确认是否进行下一次点击. 这也就是300ms的由来!

On touch devices, a click event has a 300ms delay before firing. The reason for this the delay is that browsers need that buffer to make sure you aren’t going to double-tap on anything. The result is that if you use click events blindly, that delay creates a noticeable lag as users interact with elements on your page.

二. 如何阻止双击缩放

  1. 通过阻止双击事件来实现, 不建议
  2. css设置pointer-events:none 来禁止触发鼠标或者触屏事件
  3. 通过`来实现, user-scalable 用来阻止窗口缩放,建议使用

三. tap与click事件的关系

说到移动端事件,那么在移动端的一次触碰,会触发哪些事件呢? tap事件是什么, 和click事件有哪些关系?
由于历史原因,因此在移动端进行触碰的时候,也会触发鼠标的事件,具体事件类型和触发顺序如下:
touchstart > touchmove > touchend > mouseover > mouseenter > mousedown > click , 一次触碰及从touchstart 开始,到click结束.
说起tap事件, 最常见的就是zepto的tap事件,通关zepto的源码,tap事件是通过touchstart/toutchend来模拟的.具体可以看看zepto的源码

四. 常见情景

Z轴: A页面上出现弹窗B时, 给弹窗B元素C绑定tap事件,当点击元素C时,会触发页面A的input[type=text/file/]的focus, textarea 的focus, 绑了事件的元素执行回调.

五. 解决方案

Fastclick(Github上Star数 11787)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
// 绑定事件到document.body
FastClick.attach(document.body);
}, false);
}

// 判断是否需要使用FastClick
if (FastClick.notNeeded(layer)) {
return;
}

// 所有pc浏览器
// 浏览器不支持ontouchstart
// 安卓中chrome(all versions)meta中有user-scalable="no"属性
// 安卓中chrome 32+ meta中有width=device-width 属性
// BlackBerry 10.3+
// Firefox 27+
// 有-ms-touch-action: manipulation属性的IE10
// 有touch-action: manipulation属性的IE11

FastClick.prototype.onTouchStart = function(event) {
var targetElement, touch, selection;

this.trackingClick = true;
this.trackingClickStart = event.timeStamp;
this.targetElement = targetElement;

this.touchStartX = touch.pageX;
this.touchStartY = touch.pageY;

// this.tapDelay = options.tapDelay || 200;
// Prevent phantom clicks on fast double-tap (issue #36),
if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
event.preventDefault();
}

return true;
};

// Prevent the actual click from going though - unless the target node is marked as requiring
// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
if (!this.needsClick(targetElement)) {
event.preventDefault();
this.sendClick(targetElement, event);
}


//这个事件会在onTouchEnd中用到,经过一系列的判断,符合条件,调用这个模拟事件
FastClick.prototype.sendClick = function(targetElement, event) {
var clickEvent, touch;
//创建一个鼠标事件
clickEvent = document.createEvent('MouseEvents');
//初始化鼠标事件
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
//触发这个事件
targetElement.dispatchEvent(clickEvent);
};
  1. yocto-touch模块(基于zepto的支付宝移动端库)
    新增模块,提供tap事件,用于解决移动端click事件300ms延迟以及点透等问题。
    相比Zepto的tap事件,Yocto里面的tap事件会阻止原生的click事件防止点透的发生,同时会根据点击的元素去触发对应的默认事件,比如label,input等元素。
  2. 牛逼的话自己写个JS玩玩.
  3. 浏览器厂商解决方案
    1) Android 平台上的 Chrome 和 Firefox 浏览器会禁用双击缩放功能;
    2) 如果站点内配置了内容为 width=device-width 的标签,Chrome 32 及以上版本的浏览器也会禁用双击缩放功能;
    3) Internet Explorer 则对元素引入了全新的 CSS 属性,touch-action,若将其置为 none,也会取消该元素上的点击延迟;

Chrome and Firefox for Android have, for some time now, removed the 300ms tap delay for pages with this:

Pages with this cannot be zoomed, therefore “double-tap to zoom” isn’t an interaction, therefore there’s no need to wait for double-taps. However, we also lose pinch-zooming.

Firefox has a ticket for it and currently avoids the 300ms delay for unzoomable pages.

On iOS Safari, double-tap is a scroll gesture on unzoomable pages. For that reason they can’t remove the 300ms delay. If they can’t remove the delay on unzoomable pages, they’re unlikely to remove it on zoomable pages.

Windows phones also retain the 300ms delay on unzoomable pages, but they don’t have an alternative gesture like iOS so it’s possible for them to remove this delay as Chrome has. You can remove the delay using:

html {
-ms-touch-action: manipulation;
touch-action: manipulation;
}
Unfortunately this is a non-standard Microsoft extension to the pointer events spec. Also, programmatic fixes like this are opt-in by the developer, whereas the Chrome fix speeds up any existing mobile-optimised site.

六. 总结

虽然 JavaScript 的方案很好地解决了延迟问题,但毕竟只是临时的措施。浏览器本身所提供的方案更属长久之计,在今天, 保持缩放功能, 消除300ms延迟 is our best option !

参考文章:
Avoiding the 300ms Click Delay, Accessibly
300ms tap delay, gone away
也来说说touch事件与点击穿透问题
The 300 ms Click Delay and iOS 8

转载申请

本作品采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,文章内图片请保留全部内容。