欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

zepto.js中的Touch事件

程序员文章站 2022-03-02 20:26:07
...

  在移动端经常需要处理滑动事件,比如banner图的左滑右滑,都需要事件响应,这在PC端是没有这类事件的。而在zepto.js中,下载下来的代码也并没有自动加上对这类事件的支持,但是有提供相应的模块,需要你自己加上去(下表中最后一个touch模块):

module default description
zepto 核心模块;包含许多方法
event 通过on()off()处理事件
ajax XMLHttpRequest 和 JSONP 实用功能
form 序列化 & 提交web表单
ie 增加支持桌面的Internet Explorer 10+和Windows Phone 8。
detect   提供 $.os和 $.browser消息
fx   The animate()方法
fx_methods   以动画形式的 showhidetoggle, 和 fade*()方法.
assets   实验性支持从DOM中移除image元素后清理iOS的内存。
data   一个全面的 data()方法, 能够在内存中存储任意对象。
deferred   提供 $.Deferredpromises API. 依赖"callbacks" 模块. 
当包含这个模块时候, $.ajax() 支持promise接口链式的回调。
callbacks   为"deferred"模块提供 $.Callbacks
selector   实验性的支持 jQuery CSS 表达式 实用功能,比如 $('div:first')el.is(':visible')
touch   在触摸设备上触发tap– 和 swipe– 相关事件。这适用于所有的`touch`(iOS, Android)和`pointer`事件(Windows Phone)。

  这些模块的代码可以直接加在zepto.js的最底部,像拼积木一样,选择模块然后加上去拼合一份适合自己项目的加强版zepto.js。

  关于代码中是如何实现的事件这里不做过多解读,核心肯定是通过touch坐标的差值判断到底是左滑还是上滑。这里主要讲的是touch模块中的一个坑,那就是会和国内的主流手机浏览器的默认事件冲突。具体表现就是,给一个元素绑定左滑动后,在UC浏览器中滑动该元素会出现页面被切换的情况。

  简单的测试发现:UC/QQ都与touch事件发生冲突,安卓自带浏览器和谷歌浏览器不会有此现象。touch这个脚本本来就是外国人写的,当然就无视中国特色了。这里对touch做了一些简单的修改,达到避免这种冲突发生。

  首先引入zepto(已包含touch)之后,代码如下:  

$('#div').swipeLeft(function(e){
     alert(1);	  	 
})

  在PC上用谷歌开发工具模拟器看,没有任何问题。

  在手机上用UC或者QQ看,悲催了,页面直接被滑跑了。你可以试试,体验一下总比别人告诉你印象深刻。

  很明显,这里需要阻止默认事件,比如这样改一改:  

$('#div').on('touchstart',function(e){
   e.preventDefault();
}).swipeLeft(function(e){
   alert(1);	  	 
})

  手机上便可以弹出1。然后事情并没有结束。

  首先这样写太丑陋了,每个swipe之前都要去阻止默认事件,更加难以接受的是,这个时候我上下滑动该元素也会阻止默认事件,也就意味着,当页面需要正常上下滚动的时候,手指在该元素上将滑动不了!

  好吧,那我就去改touch。

  首先,找到touch事件快捷绑定的代码块:  

;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
    $.fn[m] = function(callback){  
      return this.bind(m, callback) 
    }
})

  修改如下:

;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
    $.fn[m] = function(callback){  
      if(!this.data('swipeEvents')){
        this.data('swipeEvents',m);
      }else{
        this.data('swipeEvents',this.data('swipeEvents')+'|'+m);
      }       
      return this.bind(m, callback) 
    }
})

  目的:给所有直接调用swipe的元素,默默的标识上一个数据,该数据就是本元素被绑定的swipe事件的名称,绑定多个swipe时以 | 隔开。

  第二步:找到给body绑定的事件冒泡的touchmove事件代码:

.bind('touchmove', function(e){
      cancelLongTap()
      touch.x2 = e.touches[0].pageX
      touch.y2 = e.touches[0].pageY
})

  修改为:

.bind('touchmove', function(e){
      cancelLongTap()
      touch.x2 = e.touches[0].pageX
      touch.y2 = e.touches[0].pageY
      
      if(touch.el&&touch.el.data('swipeEvents')){  
        if(Math.abs(touch.x2-touch.x1)>4&&/Left|Right/.test(touch.el.data('swipeEvents'))){
          e.preventDefault();
        }else if(Math.abs(touch.y2-touch.y1)>4&&/Up|Down/.test(touch.el.data('swipeEvents'))){
          e.preventDefault();
        }
      }      
 })

  目的:只要该元素绑定了swipe事件,并且滑动轨迹和swipe事件匹配上,则阻止默认事件。这样,匹配不上的将不被阻止,比如你只是绑定的左右滑动,当touch是上下滑动的时候,不会执行到preventDefault。

  经过UC和QQ测试都没有问题。

  但是,事情还是没有结束。因为,你只要换个方式绑定swipe就挂了,比如这样子:  

$('#div').on('swipeLeft',function(e){

  因为我们只给$.fn[swipe]的时候做了处理,也就是元素直接调用siwpe方法时,才会有效,一旦调用on方法就嗝屁了。当然,一般元素直接调用swipe就可以了,但是如果想用到事件委托,给大量元素批量绑定的时候就必须用到on啊,这怎么办?不好意思,我这里没有给出好办法,因为on这个方法不好改也不能改,改touch已经是心惊胆战了,直接改zepto水平不够。所以暂时还没想到最好的办法,谁知道完美解决办法的请留言。