原因
无键盘全触屏手机初期,页面全是为PC端设计的(没有设置<meta name="viewport">
),iPhone作为全触屏手机始祖,为了便于用户浏览网页引入了双击缩放功能。手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。如何判断用户是单击还是双击?当用户点击屏幕时,会等待300ms,如果300ms内再次点击,则判定为双击,如果300ms内没有再次点击,则判定为单击。所以如果用click监听用户点击链接或元素,click事件会在300ms后再触发。
现象
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
body {
font-size: 60px;
}
</style>
</head>
<body>
<div id="delay">有延迟</div>
<div id="no-delay">无延迟</div>
<a id="link1" href="#1">链接1</a>
<a id="link2" href="#2">链接2</a>
<div id="log"></div>
<script>
const log = str => $('#log').innerText = str;
const $ = s => document.querySelector(s)
let t1, t2;
$("#delay").ontouchstart = function () {
t1 = Date.now(); //记录点击事件
console.log(t1);
}
$("#delay").onclick = function () {
log(Date.now() - t1); //打印出click触发时间,会延迟300ms左右
console.log(222);
}
$('#no-delay').ontouchstart = e => {
log('touchstart无延迟')
}
$('#link1').ontouchstart = e => {
t2 = Date.now()
}
$('#link2').ontouchstart = e => {
t2 = Date.now()
}
window.onhashchange = () => {
log(`link ${location.hash} : ${Date.now() - t2}ms`)
}
</script>
</body>
</html>
上面代码,KaTeX parse error: Expected 'EOF', got '#' at position 3: ("#̲delay").ontouch…("#delay").onclick 记录了click被触发的时间,#log打印出延迟时间,一般在300ms左右。也就是说click事件被触发事件比用户触摸屏幕事件晚300ms,这样会造成一种延迟、不顺畅的感觉,体验不好。
解决
- 设置meta:
<meta name="viewport" content="width=device-width">
,经过测试,在android手机只要添加name=viewport的meta即可消除延迟, 在ios上必须设置width=device-width。 一般做移动端自适应都会加上这句话,所以大部分情况下,加上这句话就能解决问题。 - 其他方法:fastclick库(目前也不经常使用,因为大部分情况下,第一种方式能解决问题),或者不用click用touchstart(不推荐,用户滑动页面也会触发touchstart,又会产生新问题)。
fastcilck
- 原理:在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后真正的click事件阻止掉。
- 简单实现:
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)
说明
即便解决了300ms延迟,click事件的触发还是会有一定延迟。原因:事件的触发顺序为touchstart -> touchend -> click,也就是说用户手指离开屏幕才触发click,用户点击到手指离开总有个间隔,所以总会有一点延迟。