tap事件 VS click事件
tap
事件通常用于移动端开发,当用户用手指在屏幕上快速轻点时触发,它的触发相对迅速,没有明显的延迟
click
事件在移动端可能会有 300ms 左右的延迟,这是为了处理移动端浏览器可能的双击缩放操作
早期的移动浏览器为了区分用户是单击操作还是双击操作,会等待约 300ms 来判断是否为双击
如果是双击,就进行页面缩放,如果不是,则触发
click
事件
为了在移动端快速响应点击事件,tap
应运而生,它既可以在触摸屏设备通过手指轻点触发,也可以在普通的鼠标点击上触发。在移动端开发中,tap
事件通常被用来替代click
事件,因为它能够更好地适配触摸屏设备
tap事件是怎么实现的?
tap
事件是一个封装了click
和touch
事件的高级事件,模拟它需要解决两个问题,其一是解决click
300ms的延迟,其二是模拟手指按下和抬起。
- 首先,为文档对象
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事件
bindtap
和catchtap
都是当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数
区别在于bind
事件绑定不会阻止冒泡事件向上冒泡,catch
事件绑定可以阻止冒泡事件向上冒泡,
用代码呈现如下:
if (Math.abs(endX - startX) < 10 && Math.abs(endY - startY) < 10 && (endTime - startTime) < 300) {
console.log('tap 事件触发');
event.stopPropagation(); // 阻止事件冒泡,实现catchTap
}
bindtap
(冒泡事件)适用场景:
- 当父元素和子元素都需要对同一类型的点击事件做出不同的响应时,可以使用
bindtap
。例如,一个列表项的父容器和子元素都有各自的点击逻辑 - 对于多层嵌套的组件结构,如果希望事件能够向上传播,让父级组件能接收到并处理事件相关信息,适合使用
bindtap
catchtap
(阻止冒泡事件)适用场景:
- 当希望子元素的点击事件独立处理,不希望其向上冒泡影响父元素的相同事件时,使用
catchtap
。比如一个弹出框中的关闭按钮,点击关闭按钮时,只希望执行关闭弹出框的操作,不希望触发父容器的点击事件 - 当处理一些需要立即响应且不希望受到其他元素干扰的操作时,例如表单提交按钮的点击事件,为了确保点击操作的唯一性和独立性,使用
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.downloadFile
或 wx.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
的成功回调中我们会拉起向用户显示成功的提示,例如 “数据加载成功” 、“操作已完成”等,那么失去点击态可能无法显示提示信息,用户没有看到点击态的反馈,误以为第一次点击没有生效,从而再次点击并触发重复的网络请求