移动端的touch事件处理

16

简要的探讨一下移动端 touch 事件处理几个坑,以及相应的简单处理方法。

click 穿透

假设有个弹出层,上面有个关闭的按钮支持 touchend 触发后关闭,若正好下方有个元素支持 click 事件,在弹出层关闭后将会在下方元素触发 click 事件。这种效果肯定不是我们需要的,而且我们无法确定合适会在上方出现一个支持 touch 的弹出层,所以我认为最好的处理方式是禁用所有元素的 click 事件,相比 click 需要长达 1s 的触发时间,使用 touchend 可以获得更好的体验。

tap 事件的判定

一个正确的 tap 事件应当满足一下条件:

  1. 用户手指从屏幕移开时触发
  2. 不能在用户移动手指时触发(防止和滚动、拖拽事件的冲突)
  3. 多个手指同时触摸屏幕时不能触发
  4. 不应该触发 click 事件

具体实现代码可以参考 tap-event

使用原生的滚动事件

Android 4.0 以下是不支持原生的 webview 滚动的,所以只能使用 iscroll 之类的工具来模拟元素滚动。它的缺点就是有些过于的复杂,所以我还是会在条件允许的情况下使用原生的滚动。

启用原生滚动只需要给外层元素加上样式 -webkit-overflow-scrolling: touch; 即可,如果你的监听函数比较占用资源我们可以通过一个简单的 buffer 函数来限制它的触发间隔,例如:

function buffer(fn, ms) {
  var timeout;
  return function() {
    if (timeout) return;
    var args = arguments;
    timeout = setTimeout(function() {
      timeout = null;
      fn.apply(null, args);
    }, ms);
  }
}


document.querySelector('.scrollable').onscroll = buffer(onScroll, 100);

另外的建议就是不要在可滚动元素上使用阴影样式(text-shadow 和 box-shadow),因为它们非常影响性能,而且看上去也不怎么美观。

还有需要注意的是如果你需要启用apple-mobile-web-app-capable, 注意将apple-mobile-web-app-status-bar-style设置为black-translucent,否则会出现还差 22 像素滚动不到头的坑爹 bug。

禁用页面整体拖动

IOS下默认情况下用户的拖动操作在scroll滚到头以后会导致整体页面的滚动,一种方式是禁用掉 document 的 touchmove 原生触发

document.addEventListener('touchmove', function(e) {
  e.preventDefault();
});

此时原生的滚动是无法工作的,解决办法就是禁用滚动元素的 touchmove 事件冒泡

scrollable.addEventListener('touchmove', function (e) {
   e.stopPropagation();
});

另一种方式是判定滚动元素滚到头之后禁用掉默认的处理

var el = document.querySelector('.scrollable');
var sy = 0;
events.bind(el, 'touchstart', function (e) {
  sy = e.pageY;
})

events.bind(el, 'touchmove', function (e) {
  var down = (e.pageY - sy > 0);
  //top
  if (down && el.scrollTop <= 0) {
    e.preventDefault();
  }
  //bottom
  if (!down && el.scrollTop >= el.scrollHeight - el.clientHeight) {
    e.preventDefault();
  }
})

我个人倾向于第二种方案,因为如果单纯的禁用 document 的 touchmove 监听,会导致一些处理的失效,比如说上面提到的 tap-event 模块。

拖动方向与距离

通过 event 的 pageX 和 pageY 属性即可计算,可参考 hammer.js

你可能感兴趣的

11 条评论
dingquantracy · 2015年11月04日

touchmove中执行e.preventDefault() 事件 会报这个错误,求解。 Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.

+2 回复

Wangbaogang · 2016年10月19日

你这个问题我也遇到过,在chrome中手机模式下。但是第二天再看就好了,感觉很诡异/(ㄒoㄒ)/~~

+1 回复

0

为什么重启浏览器就好了

费米子 · 2017年01月18日
1

给html元素添加一个css3属性: touch-action: none;即可解决这个问题

李文雄 · 2018年10月24日
0

@李文雄 ios不支持啊

铁皮饭盒 · 2018年11月22日
giauque · 2015年10月31日

禁用页面整体拖动 事件的方法,大神能否写具体点,我在解决touchmove时间冒泡的时候调用了您的代码,提示我 scrollable是未被确认的。这块实在不会了,求大神指教。是否直接用您的方法,不需要加载其他插件即可解决呢?
使用原生滚动报错:Uncaught ReferenceError: onScroll is not defined
使用禁用页面整体拖动报错:Uncaught ReferenceError: scrollable is not defined

我是个菜鸟,谅解下!

回复

chemzqm 作者 · 2015年11月01日

scrollable 未定义,你先定义它

回复

giauque · 2015年11月02日

您有QQ神马的么,可以加您请教么?

回复

chemzqm 作者 · 2015年11月02日

QQ我不用的,建议你多去 https://developer.mozilla.org/en-US/docs/Web 学习

回复

无名小子 · 2016年10月29日

想问下: 【禁用页面整体拖动】目录下的 events 是什么?

回复

chemzqm 作者 · 2016年11月01日

回复

载入中...