移动端tap事件点透问题的解决方法

2017-10-16 | 移动端

# tap

在Web移动端,使用click会有300ms的延迟时间。这是因为,浏览器在等着看你是不是在执行双击。这300ms延迟给带来了不好的用户体验,特别在切换选项卡的时候,会让人觉得是不是没有按到。

Zeptotouch模块的tap事件解决了300ms的问题,页面的响应速度给人感觉快了,不过使用tap事件却会带来其他问题,比如“点击穿透”。

# 什么是“点击穿透”?

“点击穿透”常见的发生场景:

假如页面上有A和B两个元素。B元素在A元素之上。我们在B元素的tap事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。这时,我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。

# 为什么会发生“点击穿透”?

通过看Zeptotouch.js的源码,我发现tap事件不是移动端原生的事件,而是通过绑定在document上的底层touchstart,touchmove,touchend,touchcancel事件来封装实现的自定义事件,除了tap还有其他的swipe,doubleTap,longTap等自定义事件。

touchstarttouchend都是立即触发的,所以tap事件的响应也会很快,这时候位于页面上层的B元素会很快隐藏。

但是click事件有300ms的延迟,当tap事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了B元素下的A元素上。

如果A元素没有绑定click事件,往往页面不会有什么反应,不过如果A元素是一个a元素,页面就会发生跳转;如果A元素是一个input元素(或者select),元素就会聚焦弹出键盘。

# “点击穿透”的解决方法

# 只用tap

将页面所有click事件全部换成tap,这样就算发生“点击穿透”,因为页面没有绑定click事件,所以对页面也不会有影响。不过a元素还是会跳转,我们可以这样做:

$(function() {
  $('a')
    .click(function(e) {
      e.preventDefault(); // 阻止a标签跳转
      return false;
    })
    .tap(function() {
      var href = this.href;
      href && (location.href = href); // 在tap事件中跳转
    });
});

代码阻止了a标签click的默认行为,然后通过js来实现跳转。

不过上面这一招对input元素(或者select)不起作用,这些元素还是触发默认的行为。

# tap延迟300ms+再隐藏元素

$('#hideDiv').on('tap', function(e) {
  var _this = this;
  setTimeout(function() {
    _this.style.display = 'none';  
  }, 350);
});

这一招即使click有300ms的延时,但是300ms后click也是派发到当前元素上,而不会派发到下面的元素上。