先看一下测试代码
<body>
<div id="delay">click有延迟 </div>
<div id="no-delay">touchstart无延迟</div>
<div> <a id="link1" href="#1">链接1</a> <a id="link2" href="#2">链接1</a></div>
<div id="log"></div>
<!-- 方便手机调试 -->
<style>
body {
font-size: 60px;
}
</style>
<script>
const $ = s => document.querySelector(s)
const log = str => $('#log').innerText = str
let t1, t2
$('#delay').ontouchstart = e => { //记录时间
t1 = Date.now()
}
$('#delay').onclick = e => { //先触发ontouchstart,再 ontouchend,最后触发 click
log(Date.now() - t1) //click 执行时,和 ontouchstart 差多少时间,用 log 展示到页面上
}
$('#no-delay').ontouchstart = e => {
log('touchstart无延迟') //ontouchstart 直接输出
}
$('#link1').ontouchstart = e => {
t2 = Date.now() //点链接1时,hash 会切换,
}
$('#link2').ontouchstart = e => {
t2 = Date.now() //点链接2时,hash 会切换,
}
window.onhashchange = () => { //调用 onhashchange ,现在的时间减去触摸的时间
log(`link ${location.hash} : ${Date.now() - t2}ms`)
}
</script>
</body>
点击 '#delay' 元素时,首先触发的是 ontouchstart ,再触发 onclick,中间是有大约 300ms 的延迟时间
同样,点击 '#link1' 元素时,url 的变化,也是有 300ms 的延迟时间
这就是所谓的在移动端有 300ms 延迟(在手机端可能会是 400ms 这个不确定)
延迟的原因
以前的网页全都是适配的 PC 浏览器,全都是给 PC 看的,这个页面放到手机上,用起来就会不方便,为了方便用户快速的去缩放页面,Iphone 引入了双击缩放功能,提高用户体验,但是问题来了,当我在屏幕上点击一个元素时,浏览器怎么知道我是单击还是双击,它会立刻跳转去响应呢,还是等着双击,所以此时浏览器只能等,在300ms 以内,如果没有下一次点击,浏览器认为是点击不是双击,不需要页面缩放,去响应点击事件,在 300ms 以内,又点击了一次,表示此操作是双击(而不是跳转或事件单击),做了页面缩放,浏览器必须等 300ms ,才能知道用户的真实行为,对于普通的事件响应,只有 300ms 后才知道,所以延时 300ms , 其他设备浏览器也开始模仿 iphone(iphone是无键盘全触屏手机的始祖)
现在是 9102 年了,大部分浏览器都没有此设置了,也就没有延迟的问题了。不过还是可以了解下具体的解决方案。
如何解决呢
方法1:在 ios android 都会生效,必须 width = device-width ,若设置为 width = 800px , android 生效,ios 不生效,所以不能写其他值
<meta name="viewport" content="width=device-width">
延迟会消失(其实没有消失,只不过延迟时间变短了)
为什么还会有 73ms 的延迟呢
正常情况下,用户点击 'click 有延迟' 时,再松开,是有时间的(可能几十毫秒),会触发 touchstart , touchmove, touchend, touchstart 和 touchend 之间是有时差的,touchend 之后,click 事件才会触发。这个时间不是设备故意设置的 300ms 了,
方法2:使用 fastclick 库,或者不用click用touchstart
若使用 touchstart ,可能拖拽缩放是有问题的,比如手指一触碰就会响应... 而且没有按下的效果(体验差)
fastclick 的使用及原理
- 若定义meta 时,width 是其他值时,还是存在延迟问题
- 2017年以前,很多机型不支持 meta
所以此时需要 fastclick
如何使用
引入
<script src="https://cdn.bootcss.com/fastclick/1.0.6/fastclick.js"></script>
然后加上
<script>
document.addEventListener('DOMContentLoaded', function () {
FastClick.attach(document.body)
}, false)
</script>
原理
正常情况下,用户 touchstart touchmove touchend 之后,300ms 后,会触发 click 事件
在检测到 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)