tap事件详解

1 阅读5分钟

tap事件 VS click事件

tap 事件通常用于移动端开发,当用户用手指在屏幕上快速轻点时触发,它的触发相对迅速,没有明显的延迟

click 事件在移动端可能会有 300ms 左右的延迟,这是为了处理移动端浏览器可能的双击缩放操作

早期的移动浏览器为了区分用户是单击操作还是双击操作,会等待约 300ms 来判断是否为双击

如果是双击,就进行页面缩放,如果不是,则触发 click 事件

为了在移动端快速响应点击事件,tap应运而生,它既可以在触摸屏设备通过手指轻点触发,也可以在普通的鼠标点击上触发。在移动端开发中,tap事件通常被用来替代click事件,因为它能够更好地适配触摸屏设备

tap事件是怎么实现的?

tap事件是一个封装了clicktouch事件的高级事件,模拟它需要解决两个问题,其一是解决click300ms的延迟,其二是模拟手指按下和抬起。

  • 首先,为文档对象 document 分别添加 touchstart 和 touchend 事件的监听
  • 在 touchstart 事件触发时,记录下触摸开始的时间、水平位置和垂直位置
  • 在 touchend 事件触发时,获取触摸结束的相应时间和位置信息
  • 然后通过比较触摸开始与结束位置的水平和垂直距离(判断是否在较小范围内),以及触摸的时间间隔(小于 300 毫秒),来确定是否满足快速点击(tap)的条件。如果满足,就认为是 tap 事件
document.addEventListener('touchstart', function(event) {
  // 记录触摸开始的时间和位置
  startX = event.touches[0].pageX;
  startY = event.touches[0].pageY;
  startTime = new Date().getTime();
}, false);


document.addEventListener('touchend', function(event) {
  // 获取触摸结束的时间和位置
  endX = event.changedTouches[0].pageX;
  endY = event.changedTouches[0].pageY;
  endTime = new Date().getTime();,

  // 判断是否为快速点击(tap)
  if (Math.abs(endX - startX) < 10 && Math.abs(endY - startY) < 10 && (endTime - startTime) < 300) {
    // 触发 tap 事件的处理逻辑
    console.log('tap 事件触发');
  }
}, false);

微信小程序中的tap事件

bindtapcatchtap都是当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数

区别在于bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡, 用代码呈现如下:

 if (Math.abs(endX - startX) < 10 && Math.abs(endY - startY) < 10 && (endTime - startTime) < 300) {
    console.log('tap 事件触发');
    event.stopPropagation();  // 阻止事件冒泡,实现catchTap
 }

bindtap(冒泡事件)适用场景:

  1. 当父元素和子元素都需要对同一类型的点击事件做出不同的响应时,可以使用 bindtap 。例如,一个列表项的父容器和子元素都有各自的点击逻辑
  2. 对于多层嵌套的组件结构,如果希望事件能够向上传播,让父级组件能接收到并处理事件相关信息,适合使用 bindtap 

catchtap(阻止冒泡事件)适用场景:

  1. 当希望子元素的点击事件独立处理,不希望其向上冒泡影响父元素的相同事件时,使用 catchtap 。比如一个弹出框中的关闭按钮,点击关闭按钮时,只希望执行关闭弹出框的操作,不希望触发父容器的点击事件
  2. 当处理一些需要立即响应且不希望受到其他元素干扰的操作时,例如表单提交按钮的点击事件,为了确保点击操作的唯一性和独立性,使用 catchtap 

在一个商品列表页面中,每个商品项是一个组件,包含图片和文字,点击图片时希望放大图片

此时建议使用 bindtap ,以便父容器能接收到事件做一些全局处理,例如需要显示相关提示信息、埋点记录记录用户的浏览行为数据、或者触发一些动画效果,如淡入淡出、缩放等

而点击文字时直接跳转到商品详情页(建议使用 catchtap ,确保只执行跳转操作,不触发父容器的其他逻辑)。

 <div id="productList">
    <div class="productItem">
      <img src="product1.jpg" alt="Product 1" bindtap="handleImageClick" />
      <p catchtap="handleTextClick">商品 1 详情</p>
    </div>
    <div class="productItem">
      <img src="product2.jpg" alt="Product 2" bindtap="handleImageClick" />
      <p catchtap="handleTextClick">商品 2 详情</p>
    </div>
  </div>

    // 模拟全局处理图片点击的函数
    function handleImageClick(event) {
      console.log('图片被点击,执行全局处理逻辑');
    }

    // 模拟处理文字点击跳转的函数
    function handleTextClick(event) {
      console.log('文字被点击,跳转到详情页');
    }

微信小程序中tap事件的点击态

tap事件处理函数中进行异步请求是一个常见的行为,当 wx.request、wx.downloadFilewx.getSetting 被调用时具有点击态,则会延续该点击态至 success/complete/fail回调函数,但 为了防止敏感接口被滥用,部分API触发时会检查并消耗点击态,点击态失效时,则对应功能会受到影响

handleTap: async () => {
  // 点击态有效
  wx.request({
    url: "https://www.thissitedoesnotexist.com/step1",
    complete: () => {
      // 点击态有效
    },
  });
};

handleTap: async () => {
  // 点击态有效
  wx.openSettings({}); // 成功调用
  wx.request({
    url: "https://www.thissitedoesnotexist.com",
    complete: () => {
      // 失去点击态,因为点击态被 wx.openSettings 消耗掉了
    },
  });
  wx.openSettings({}); // 成功调用

};

如果在 wx.request 的成功回调中我们会拉起向用户显示成功的提示,例如 “数据加载成功” 、“操作已完成”等,那么失去点击态可能无法显示提示信息,用户没有看到点击态的反馈,误以为第一次点击没有生效,从而再次点击并触发重复的网络请求