touch事件的来源
- pc mousedown -> mouseup -> mousemove
- 移动端 touchstart -> touchmove -> touchend
参数获取
- touches 屏幕中每根手指信息列表
- targetTouches 和touch类似,把同一节点的手指信息过滤掉
- changedTouches 响应当前时间的每根手指的信息列表
说明:在手机上,当我们手触碰屏幕时,要过300ms左右才会触发mousedown
事件,所以click
事件在手机上看起来就像慢半拍一样
移动端click 300ms 延迟
注意:user-scalable=no 设置了这个属性之后也就不存在点击300ms延迟的问题了
基本描述
1.问题由来:
这要追溯至 2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。 这当中最出名的,当属双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。
那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。之前人们刚刚接触移动端的页面,在欣喜的时候往往不会care这个300ms的延时问题,可是如今touch端界面如雨后春笋,用户对体验的要求也更高,这300ms带来的卡顿慢慢变得让人难以接受。
2.简单总结:移动端为了展示pc端页面给用户一个放大网页的方式,所以浏览器需要判断用户的点击行为
3.代码测试:
解决方案
fastclick
实现原理:fastclick 在检测到touchend事件的时候,会通过dom自定义事件立即触发一个模拟click事件,并把浏览器在300毫秒之后真正触发的click事件阻止掉。
FastClick 的使用方法非常简单,在 window load 事件之后,在body上调用FastClick.attach()即可。
window.addEventListener( "load", function() {FastClick.attach( document.body );}, false );
attach方法虽可在更具体的元素上调用,直接绑定到body上可以确保整个应用都能受益。当 FastClick 检测到当前页面使用meta设置了user-scalable=no或者 touch-action 属性的解决方案时,会静默退出。可以说,这是真正的跨平台方案出来之前一种很好的变通方案。
就目前而言,FastClick 非常实际地解决 300 毫秒点击延迟的问题,唯一的缺点可能也就是该脚本的文件尺寸 (尽管它只有10kb)。如果你连这10kb都接受不了的话,那么移动端类库 jQuery和zepto.js都支持tap事件来解决这个问题,尽管它们的响应速度比FastClick慢一些。
穿透场景
问题说明
当有一个绝对定位或固定定位元素绑定了touch事件,那么覆盖在他只下的具有点击特性的元素也会被触发。
弹窗/浮层这种形式。当点击关闭之后,touchend首先触发tap,弹出层关闭。但是touchend后继续等待300ms发现没有其他行为了,则继续触发click,由于这时弹出层已经消失,所以当前click事件的target就在底层元素上,于是就触发了底部。整个事件触发过程为 touchend -> tap -> click。
原因:touch会先于click事件执行,当上述的这个遮罩层消失在300ms之内,那么他底下的具有点击特性的元素会被触发
代码实现 弹窗/浮层。比如:有一个遮罩层A元素,他的底下有一个链接元素B. 当这个遮罩层点击之后希望遮罩层消失,真实的状况是这时候点击遮罩层不见的同时会跳转页面。
解决方案:
- 可以在touch阶段取消掉触发的click事件
- 可以让消失的元素延迟 200-300ms ```javascript
<!DOCTYPE HTML>
<style type="text/css">
body{
margin: 0;
}
.container{
width: 100%;
overflow: hidden;
position: relative;
}
.layer-title{
width: 100%;
margin: 50px 0;
text-align: center;
}
.layer-action{
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.btn{
background-color: #08c;
border: 0;
color: #fff;
height: 30px;
line-height: 30px;
width: 100px;
}
#underLayer{
background-color: #eee;
width: 90%;
height: 500px;
line-height: 500px;
margin: 30px auto 1000px;
text-align: center;
}
#popupLayer{
/*display: none;*/
background-color: #fff;
width: 80%;
height: 200px;
position: fixed;
top: 50%;
left: 50%;
margin-left: -40%;
margin-top: -100px;
z-index: 1;
}
#bgMask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.6);
}
</style>
<div id="popupLayer">
<div class="layer-title">弹出层</div>
<div class="layer-action">
<button class="btn" id="closePopup">关闭</button>
</div>
</div>
</div>
<div id="bgMask"></div>
<script type="text/javascript" src="//cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
<script type="text/javascript">
Zepto(function($){
// 点击穿透
var $close = $('#closePopup');
var $popup = $('#popupLayer');
var $under = $('#underLayer');
var $mask = $('#bgMask');
$close.on('tap', function(e){
$popup.hide();
$mask.hide();
});
$under.on('click', function(){
alert('underLayer clicked');
});
});
</script>
``` **
tips:
- 遮罩的形成注意需要给 一个 z-index 这样才可以实现下面是透明的效果
解决方案:
poniter-events 主要属性 auto 和 none
文章