1. 300ms延迟的原因
iphone是无键盘全触屏手机的始祖,当时的网站都是为大屏幕设备所设计的(没有设置),为了便于用户阅读浏览器引入了双击缩放的功能。手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。那如何判断用户快速点击两次呢?假定用户点击后300ms内如果有第二次点击,就认为用户的目的是双击缩放而不是普通的点击页面元素。所以用户第一次点击一个链接后,必须等300ms,浏览器才能决定是否是跳转还是缩放页面。 后来全触屏手机流行后,其他设备浏览器也开始效仿
如何解决
方法1、设置meta1
<meta name="viewport" content="width=device-width">
经过测试,在android手机只要添加name=viewport的meta即可消除延迟, 在ios上必须设置width=device-width
其他方法
fastclick库,或者不用click用touchstart
2. fastclick的原理和简单实现
什么时候使用
当移动端因为一些原因未使用<meta name="viewport" content="width=device-width">的时候使用。2017年以前使用,现在一般不用。
如何使用
1 | document.addEventListener('DOMContentLoaded', function() { |
fastclick 原理
在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后真正的click事件阻止掉
实现一个简单的fastclick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const FastClick = (function(){
function attach(root) {
let targetElement = null
root.addEventListener('touchstart', function () {
targetElement = event.target
})
root.addEventListener('touchend', function (event) {
event.preventDefault()
let touch = event.changedTouches[0]
let clickEvent = document.createEvent('MouseEvents')
clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null)
clickEvent.forwardedTouchEvent = true
targetElement.dispatchEvent(clickEvent)
})
}
return { attach }
})()
FastClick.attach(document.body)
3. 什么是点击穿透现象
在移动端,当用户通过绑定touchstar事件监听函数让浮层关闭时,关闭后浮层后面对应位置页面其他元素也被点击,比如浮层的关闭按钮下是一个链接,当用户点击浮层关闭按钮浮层消失后大约300ms页面同时发生跳转。
原因分析
触屏设备为了区分用户双击缩放,对click做了300ms延迟触发,因此用户在移动端触屏设备的操作流程以及事件触发为:
- 手指触摸屏幕到屏幕,触发 touchstart
- 手指在屏幕短暂停留(如果是移动,触发 touchmove)
- 手指离开屏幕, 触发 touchend
- 等待约300ms,看用户在此时间内是否再次触摸屏幕。如果没有
- 300ms后 在用户手指离开的位置触发 click事件
击穿的根源在第4步。当用户用户点击关闭让遮罩隐藏后,浏览器在300ms后在原来用户手指离开的位置触发click事件,此时遮罩不存在了,自然“点击”到后面的元素。
解决方法
方法1
设置<meta name="viewport" content="device-width"> 。表面上看起来解决了300ms延时,就能解决穿透。实际上这种方法不可行。因为click事件是在touchend事件之后,更晚于touchstart(间隔了60~100ms),如果在touchstart 里绑定关闭了浮层,约100ms后 click事件触发的时候仍然会引发问题。
方法2
关闭浮层用click,不用touchstart。 可行。因为浮层关闭监听的是click,当浮层消失后不会再在原来的位置再冒出个click
方法3
浮层关闭的时间延长。可行。当用户触发touchstart关闭浮层时,浮层可以使用渐变消失(可设置浮层关闭动画在300ms以上),此刻click事件在原来的位置触发时点击的还是浮层,而不是浮层后面的链接
方法4
在ontouchstart 里阻止默认事件。可行。 这样可以阻止click的触发