做者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致。此文的fastclick理解上在看过博客园各个大神的文章后对我确实有很大的帮助,固然,个人某些观点可能不是很准确甚至有错误,欢迎讨论,白天基本在线。css
能够结合上一篇JavaScript事件详解-原生事件基础(一)综合考虑
源码暂且不表,github里还有中文网站都能下到最新版的zepto。整个event模块不长,274行,咱们能够看到,整个event模块,事件绑定核心就是on和off,还有一个trigger用来触发,类观察者模式,能够先看看汤姆大叔的深刻理解JavaScript系列(32):设计模式之观察者模式,其他皆为实现的处理函数。
首先来个demo:html
$("#btn").on("click",function(event){ console.log(event); })
一个简单的click事件监听示例。
根据event模块中对于事件的使用来看:
java
能够看到,绑定函数有五个参数:ios
根据参数,咱们能够很轻易的将on分为几部分(上图所示):css3
autoRemove,若是one为true,也就是只想使用一次,那么使用remove,并经过apply,给callback设立event对象;
而delegator中,若是selector是绑定元素的子节点,zepto以event.target为目标元素,判断是否触发节点的父级和传入的selector一致,上下文是遍历以后的节点。而后建立一个该事件对象的副本(createProxy),返回compatible()函数处理的event,固然,最后都会经过add()来进行注册:
git
首先是zid,zepto里面有个handlers对象,用于存放处理过的事件对象,_zid初始值为1,每次会按照值存入handlers,而且修改event对象中的_zid,每次存入的是一次绑定的全部事件:
github
由于每次使用$()建立的zepto对象都是新的,用handlers创建队列才能更好的进行管理。
以后就是对于以空白字符形式(/\s/)进行分割的字符串的处理,内部建立了handle,注意其parse方法是内部方法,而不是Date.parse()。chrome
前面也说过,冒泡事件会有反作用,mouseover和mouseout,若是只是简单的节点,没有问题,但有了子节点以后。原先监听父节点的事件,会在鼠标移过去时再次触发。这是因为监听的是整个父节点,而移动到子节点时,子节点并无事件,因此向上冒泡所形成的bug,而在DOM3级中,新定义了两个不冒泡的事件:mouseenter,mouseleave,使用这两个事件,能够解决这一问题。而在zepto中,使用了relatedTarget属性,并使用contains判断触发的节点在不属于移出(mouseover),移入(mouseout)时,才执行回调。且对不支持mouseenter和mouseleave事件的状况进行了兼容。设计模式
而后就是调用addEventListener,开始监听,这里没有作IE的兼容。事件句柄随着handler插入handles中,为以后的remove作准备。这里的proxy是对于event的扩充,也是添加了当return false时,调用
preventDefault()和stopPropagation()。api
能够看出来,和on是对应的写法,一样能够分三部分,只不过这里的功能是移除监听而已。
就直接到了remove,这里主要作的就是根据传入的event和selector,用findHandlers进行查找,而后删除handlers中对应的事件,同时调用removeEventListener来移除事件处理程序。
这里使用的是createEvent()和initEvent(),这里的事件类型若是不是specialEvents中定义的MouseEvents,就会变成默认Events(DOM3)。
看下自定义的事件对象:
能够发现,由于使用compatible()封装了一下event,因此会有zepto新增的属性,以及咱们传入的props属性。
咱们知道,DOM3级中提供的触发事件的api是dispatchEvent()(低版本IE中是fireEvent()),而zepto这里也是同样:
能够看出,对于参数event为对象时进行了处理,意味着能够直接使用trigger建立+触发,支持dispatchEvent的状况下,会直接触发,若是不是DOM节点,则使用triggerHandler()来触发。
能够看出,若是节点在以前绑定了其余事件处理程序且使用过stopImmdiatePropagation(),则也不会再触发自定义事件。
demo:
这实际上是个独立的函数,在add()函数中使用的proxy,是handler.proxy()函数,与这个无关。这个函数起到的做用很相似extend,只不过它扩展的是上下文(执行环境)。
我却是以为,最关键的就是
fn.apply(fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments)) $.proxy.apply(null,args);
zepto实际是为了能在手机上轻量级的使用而创造的,为了使用上的不卡顿,手机上不能用click事件,有延时,缘由很少说了,写个小demo,能够看看pc和手机的点击事件耗时区别:
touch和click demo
若是pc端,最好用chrome,没大考虑兼容。用开发者工具也能模拟touch事件。
因此zepto提供了touch模块,我先本身模拟了下tap事件,连着又自定义了一些事件:
touch和tap demo
个人思路是,touch事件触发的很快,那么就用touch事件来模拟click,以touchstart和touchend为开始和结束,只考虑了单指的状况,touch事件自己就有:
我没有把touch的属性填入自定义的事件里,就一层,因此也没考虑冒不冒泡了,后面能够完善下,tap却是还好,dbltap稍稍耗了点时间。
整个touch模块也很简单,仍是先从入口开始:
能够看到zepto新增了这些事件,并作了简写的处理,整个部分最重要的是给document绑定了touch,MSpointer,pointer,MSGestureEvent的触摸事件,不过上面的
来看,应该有setTimeout,下面的cancelAll()中也有使用clearTimeout。
先看touch事件,其余的反正是兼容。
zepto定义的局部变量touch中有四个值,x1,x2,y1,y2,应该是用来记录第一个触发的点和第二个触发的点,果真在监听touchmove事件的回调里,使用了两个点来计算偏移,不过这里是将途中全部偏移量都与初始值进行比较,而后汇总。
在touchend中,对于swipe(滑动)和tap(点击)进行了处理,由于deltaX和deltaY须要在30*30的范围内才会被触发,可是它的偏移量是move移动的总和,因此在触发时容错率低,也就是很差点出来,相比较其余操做而言。
在touchcancel的处理中,清除全部延迟操做。
同时其整个操做实际上是绑定在了document上,因此使用时若是有其余的touch事件也绑定在了doucment上,而且取消了冒泡事件,则以后的全部操做都会失效。
专门写了下demo来测试点透问题,点透事件的发生。这里也是以前的click300ms的延时带来的问题,若是最上层始终存在还好,就怕是点击消失的状况,那么若是上层用的是touch事件,下层是a标签,input或者绑定了click事件的节点,则也会被触发,只能说zepto的touch事件还须要咱们本身来扩充和完善。
在经过对javaScript事件的详细学习以后,仍是有不少途径去解决这个问题的,好比:
使用十分简单,只要FastClick.attach(document.body);
就好,在github上也介绍了另两种方法,能够不使用fastClick来快速点击。
具体的能够直接去它的github地址看https://github.com/ftlabs/fastclick。
我修改了下以前的例子,新增了一个点击事件的例子,能够看出,基本比touchend慢1ms。并且使用了click事件,因此也不存在点透事件。
一样从入口进入,在代码内部,先实例化FastClick(),它有两个参数:layer和options,layer也就是咱们以前传的document.body,
在FastClick()函数中,能够先找到
至关于document.body上的节点的click,touch事件的监听都会被FastClick内部的事件给处理掉,若是layer上有onclick事件,一样会被oldOnClick复写。
在pc上不用多说,绑定操做的节点确定先经过对click的监听来处理,
这里的trackingClick应该是若是以前页面中的touchend被UI事件阻塞(能够简单模拟下,也就是出现字符被选中的状况),从新置空它。
而在手机上,最早触发的操做一定是touchstart和touchmove,touchend。
这里的targetElement很重要,实际上是上面提过的event的target,实际触摸的节点目标,这个属性会在接下来的move,end中用来判断是否中间换过节点,并在sendClick中用来真实的触发事件
返回touchstart中,兼容先不看,这里对于初始节点也作了存储,touchStartX,touchStartY
。
而接下来的对于double-tap的处理,其实须要看需求,若是是默认值,那么200ms内的第二次点击会禁止触发其默认事件event.preventDefault()
,在手机上就没法触发双击事件。
而后就是touchmove,这里却是很短,只是对于targetElement作了个判断,是不是同一个触摸节点。
在touchend中,一样略过兼容不看,不过我却是又学到了一个原生api,document.elementFromPoint
,传入x,y来找到节点,只是认识了一下,实不实用不表。
其实一共有三种方式来操做fastclick内部自定义的事件,不过当targetTagName=label时,这里focus了一下,把这里的触发放到了needsClick中而已,而在这两块中,能够知道,若是给className中加入needsFocus
和needsClick
,则会中断fastclick,使用原生事件:
needsFocus:第一个判断事件从touchstart到touchend是否超过100ms,则将targetElement置空,其实就是回归原来的方法,取消fastclick的事件。而后就是focus事件
这里使用了setSelectionRange(length,length),先选中文本。
有兴趣的能够去MDN看,不过我测试了一下,上面的例子中有个input,其中确实会有http://www.cnblogs.com/vajoy/p/5522114.html#!/follow提到的问题,在快速点击时光标会直接移到最后面,而上面传入的参数都是length,天然就跑到了最后面。
再经过sendClick来触发,就会有问题,并且在触发完自定义click以后,
取消了咱们点击时带的focus操做,这里我先注释了一下代码,果真,光标先定到末尾,再定到正确位置,虽然上述博主的方法暂缓此问题,但在有些浏览器中仍然存在这一问题,而且实际上仍然是光标定到末尾,再定到正确位置,不如直接不用模拟的focus,做者本身的注释里说的是ios7中有问题,但focus中用的是deviceISIOS,而不是deviceIsIOSWithBadTarget,改这里也能解决问题。
needsClick:这里首先preventDefault,再使用sendClick,上面稍微介绍了下,这里模拟的都是MouseEvents事件,使用targetElement来触发。
那么为什么fastclick不穿透呢,我天然仍是找代码中哪里用了preventDefault和stopImmediatePropagation,在onMouse中,仍是防止快速点击而加的阻止操做,实际上阻止点透的仍然是needsClick中的event.preventDefault()
,去掉以后,页面点击仍然会点透。
知道了仍是preventDefault()起了做用,咱们再回头看touch模块,对比一下fastclick,其实二者的主体层都是差很少的,document和document.body实际在大多数状况下,可点击域都是同样,也就是说二者都绑定到了最外层上(近似),但fastclick提供了layer和options,意味着能够规避风险,而zepto的touch模块则直接绑定到了document之上,至少在使用上,并无fastclick方便,不过其定义的各类touch动做,颇有意义,在上面说过,在zepto解决点透,能够:
$("#btn").tap(function(){ // do something }).on('touchend',function(e){ e.preventDefault(); });
在读源码时,layer绑定的部分,上面有图,确实会误导。由于实际上onClick是能够不走的,之因此在手机上触发的仍然是click事件,是由于在sendClick里直接使用dispatchEvent()触发了click,因此才会从fastclick定义的this.onClick()中走,其实这一部分能够和zepto同样放入touchend模块里面。
这么看,两个代码的核心其实大体相同,所不一样的是fastclick中还加入的tap事件的focus方法,若是在zepto的touch中直接加入preventDefault(),则input没法得到焦点,因此能够引入focus事件,从而解决问题
可能会有兼容性的问题,对于blur()和focus()写完jQuery以后能够加入兼容性的写法。
如今就不会点透了。
还能够把touchstart,touchend操做放到具体的节点上,而后在节点中进行处理,不过这样会改动的比较大。zepto中有个方法$.proxy,放在这里有奇效,上面介绍过。
至于说delegate,undelegate,live,die这些代理事件,还有bind,unbind等绑定操做,其实都是on在起做用,不细说。中间去看了fastclick,以及其余事情,其实星期一就写完了,仍是基础薄弱啊。
以后是看看jQuery的事件操做,压力山大。