touch事件的来源

  • pc mousedown -> mouseup -> mousemove
  • 移动端 touchstart -> touchmove -> touchend

参数获取

  • touches 屏幕中每根手指信息列表
  • targetTouches 和touch类似,把同一节点的手指信息过滤掉
  • changedTouches 响应当前时间的每根手指的信息列表

移动端点击事件 - 图1

说明:在手机上,当我们手触碰屏幕时,要过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.代码测试:
image.png

解决方案

fastclick


实现原理:fastclick 在检测到touchend事件的时候,会通过dom自定义事件立即触发一个模拟click事件,并把浏览器在300毫秒之后真正触发的click事件阻止掉。
FastClick 的使用方法非常简单,在 window load 事件之后,在body上调用FastClick.attach()即可。

  1. 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。

移动端点击事件 - 图3

原因:touch会先于click事件执行,当上述的这个遮罩层消失在300ms之内,那么他底下的具有点击特性的元素会被触发

代码实现 弹窗/浮层。比如:有一个遮罩层A元素,他的底下有一个链接元素B. 当这个遮罩层点击之后希望遮罩层消失,真实的状况是这时候点击遮罩层不见的同时会跳转页面。

解决方案:

  • 可以在touch阶段取消掉触发的click事件
  • 可以让消失的元素延迟 200-300ms ```javascript

<!DOCTYPE HTML>

  1. <style type="text/css">
  2. body{
  3. margin: 0;
  4. }
  5. .container{
  6. width: 100%;
  7. overflow: hidden;
  8. position: relative;
  9. }
  10. .layer-title{
  11. width: 100%;
  12. margin: 50px 0;
  13. text-align: center;
  14. }
  15. .layer-action{
  16. position: absolute;
  17. bottom: 20px;
  18. width: 100%;
  19. text-align: center;
  20. }
  21. .btn{
  22. background-color: #08c;
  23. border: 0;
  24. color: #fff;
  25. height: 30px;
  26. line-height: 30px;
  27. width: 100px;
  28. }
  29. #underLayer{
  30. background-color: #eee;
  31. width: 90%;
  32. height: 500px;
  33. line-height: 500px;
  34. margin: 30px auto 1000px;
  35. text-align: center;
  36. }
  37. #popupLayer{
  38. /*display: none;*/
  39. background-color: #fff;
  40. width: 80%;
  41. height: 200px;
  42. position: fixed;
  43. top: 50%;
  44. left: 50%;
  45. margin-left: -40%;
  46. margin-top: -100px;
  47. z-index: 1;
  48. }
  49. #bgMask{
  50. position: fixed;
  51. top: 0;
  52. left: 0;
  53. right: 0;
  54. bottom: 0;
  55. background-color: rgba(0,0,0,0.6);
  56. }
  57. </style>

底层元素

  1. <div id="popupLayer">
  2. <div class="layer-title">弹出层</div>
  3. <div class="layer-action">
  4. <button class="btn" id="closePopup">关闭</button>
  5. </div>
  6. </div>
  7. </div>
  8. <div id="bgMask"></div>
  9. <script type="text/javascript" src="//cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
  10. <script type="text/javascript">
  11. Zepto(function($){
  12. // 点击穿透
  13. var $close = $('#closePopup');
  14. var $popup = $('#popupLayer');
  15. var $under = $('#underLayer');
  16. var $mask = $('#bgMask');
  17. $close.on('tap', function(e){
  18. $popup.hide();
  19. $mask.hide();
  20. });
  21. $under.on('click', function(){
  22. alert('underLayer clicked');
  23. });
  24. });
  25. </script>

``` **

tips:

  • 遮罩的形成注意需要给 一个 z-index 这样才可以实现下面是透明的效果

解决方案:
poniter-events 主要属性 auto 和 none
image.png


文章