移动端tap与click的区别 && 点透事件

移动端tap与click的区别 && 点透事件

来源: 博客园 - Wayne-Zhu

1. 移动端的问题

移动端的主要问题是 click 事件会有 300ms的延迟

主要原因是苹果手机在设计时,考虑到用户在浏览网页时需要双击放大,所以,在用户点击的 300ms 之后,才触发 click 事件。如果 300ms 之内还有 click,就会进行放大缩小。

但是,大部分时候放大、缩小是不需要的,开发者也会禁用它们,那么这 300ms 的延迟就是性能上的损耗。

如何解决这300ms的延迟?

在移动端,最容易想到的就是使用 touchend 来替代 click。但是 touchend 是存在很大问题的,因为 touchend 之前可能是 touchstarttouchmove。具体情境可能是:用户滑动页面时,不小心在一个按钮那里触发了 touchend,这样就执行了,但是用户的本意不是如此。

那么该怎么解决呢?

2. tap事件

为了减少这 300ms 的延迟,tap 事件被很多框架(如 Zepto)封装,用来减少延迟问题。tap 事件不是原生的,是封装出来的。

具体是如何实现的呢?主要考虑到下面两点:

  1. 按住的时间不能超过延时时间:因为长时间按住可能就是浏览器的复制、粘贴等操作了。
  2. 不能在页面中移动 :移动是不能触发 tap 事件的。

我们可以自己封装如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>tap</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
    <button id="btn">按钮</button>
    <script>
        function tap(ele, callback) {
            // 记录开始时间
            var startTime = 0,
                // 控制允许延迟的时间
                delayTime = 200,
                // 记录是否移动,如果移动,则不触发tap事件
                isMove = false;

            // 在touchstart时记录开始的时间
            ele.addEventListener('touchstart', function (e) {
                startTime = Date.now();
            });

            // 如果touchmove事件被触发,则isMove为true
            ele.addEventListener('touchmove', function (e) {
                isMove = true;
            });

            // 如果touchmove事件触发或者中间时间超过了延迟时间,则返回,否则,调用回调函数。
            ele.addEventListener('touchend', function (e) {
                if (isMove || (Date.now() - startTime > delayTime)) {
                    return;
                } else {
                    callback(e);
                }
            })
        }
        
        var btn = document.getElementById('btn');
        tap(btn, function () {
            alert('taped');
        });
    </script>
</body>
</html>

如上,我们就可以正常使用 tap 事件并且避免了 300ms 延迟的产生。

3. 点透问题 (Ghost Click)

如果我们在移动端所有的 click 都替换为了 tap 事件,还是会触发点透问题

实质是:

在同一个 z 轴上,z-index 不同的两个元素,上面的元素是一个绑定了 tap 事件的,下面是一个 a 标签。一旦 tap 触发,上面的元素就会 display: none(隐藏或消失)。

从上面的 tap 可以看出,有 touchstarttouchend,所以系统会在 300ms 之后触发 click 事件。此时,上层元素的 z-index 已经消失了,所以,触发了下面 a 标签的 click 事件 (注意:认为 a 标签默认是绑定了 click 事件的)。而这种现象不是我们所期待的。

4. 解决方案

解决点透问题通常有以下两种方案:

(1) 使用 fastclick 库

直接引入 fastclick 库,可以有效解决 300ms 延迟和点透问题。

javascript 复制代码
window.addEventListener("load", function () {
    FastClick.attach(document.body);
}, false);

这样就可以成功解决问题了。

(2) 添加一个延迟

对于上一个 tap 事件做延迟处理,等 300ms 之后再隐藏上层元素。

javascript 复制代码
tap(ele, function () {
    setTimeout(function () {
        ele.style.display = 'none';
    }, 300);
})

这样,过了 300ms 后,click 事件就不会触发在下面的 a 标签上了。

相关推荐
程序员黑豆8 小时前
Java类型推断完全指南:从var到菱形运算符,掌握使用限制与最佳实践
java·前端·ai编程
To_OC9 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
GreenTea9 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
浮生望9 小时前
前端API工程化:用 Mock 数据与 Axios 配置实现独立于后端的并行开发
前端
2603_965148119 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
万少10 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
波波00710 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Brown.alexis11 小时前
es6知识点1-自备使用
前端·ecmascript·es6
小爬的老粉丝12 小时前
JavaScript 纯前端预览 WPS:先识别容器,再路由解析器
前端·javascript·wps
知无不研12 小时前
lambda表达式的使用(3)
开发语言·c++·lambda