博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
zepto源码学习-04 event
阅读量:4608 次
发布时间:2019-06-09

本文共 28588 字,大约阅读时间需要 95 分钟。

之前说完$(XXX),然后还有很多零零碎碎的东西需要去分析,结果一看代码,发现zepto的实现都相对简单,没有太多可分析的。直接略过了一些实现,直接研究Event模块,相比JQuery的事件系统,zepto的设计相对简单很多,看起来也就相对轻松,整个event模块也就300行代码。

先看事件的相关接口以及用法

$.Event

$.Event(type, [properties])   ⇒ event

创建并初始化一个指定的DOM事件。如果给定properties对象,使用它来扩展出新的事件对象。默认情况下,事件被设置为冒泡方式;这个可以通过设置bubblesfalse来关闭。

一个事件初始化的函数可以使用trigger来触发。

$.Event('mylib:change', { bubbles: false })

 

$.proxy v1.0+

$.proxy(fn, context)   ⇒ function$.proxy(fn, context, [additionalArguments...])   ⇒ function v1.1.4+ $.proxy(context, property) ⇒ function $.proxy(context, property, [additionalArguments...]) ⇒ function v1.1.4+

接受一个函数,然后返回一个新函数,并且这个新函数始终保持了特定的上下文(context)语境,新函数中this指向context参数。另外一种形式,原始的function是从上下文(context)对象的特定属性读取。

如果传递超过2个的额外参数,它们被用于 传递给fn参数的函数 引用。 

bind

不推荐, 使用on代替。

delegate

不推荐, 使用on代替。

die

不推荐, 使用on代替。

live

不推荐, 使用on代替。

unbind

不推荐

undelegate

不推荐

off

off(type, [selector], function(e){ ... })   ⇒ selfoff({ type: handler, type2: handler2, ... }, [selector])   ⇒ selfoff(type, [selector]) ⇒ self off() ⇒ self

移除通过on添加的事件.移除一个特定的事件处理程序, 必须通过用on()添加的那个相同的函数。否则,只通过事件类型调用此方法将移除该类型的所有处理程序。如果没有参数,将移出当前元素上全部的注册事件。

on

on(type, [selector], function(e){ ... })   ⇒ selfon(type, [selector], [data], function(e){ ... })   ⇒ self v1.1+ on({ type: handler, type2: handler2, ... }, [selector]) ⇒ self on({ type: handler, type2: handler2, ... }, [selector], [data]) ⇒ self v1.1+

添加事件处理程序到对象集合中得元素上。多个事件可以通过空格的字符串方式添加,或者以事件类型为键、以函数为值的对象 方式。如果给定css选择器,当事件在匹配该选择器的元素上发起时,事件才会被触发(即事件委派,或者说事件代理)。

如果给定data参数,这个值将在事件处理程序执行期间被作为有用的 event.data 属性

事件处理程序在添加该处理程序的元素、或在给定选择器情况下匹配该选择器的元素的上下文中执行(this指向触发事件的元素)。 当一个事件处理程序返回falsepreventDefault() 和 stopPropagation()被当前事件调用的情况下,  将防止默认浏览器操作,如链接,同时执行阻止事件冒泡。

 one

添加一个处理事件到元素,当第一次执行事件以后,该事件将自动解除绑定,保证处理函数在每个元素上最多执行一次,使用方法和on相同

event.isDefaultPrevented v1.1+

event.isDefaultPrevented()   ⇒ boolean

如果preventDefault()被该事件的实例调用,那么返回true。 这可作为跨平台的替代原生的 defaultPrevented属性,如果 defaultPrevented缺失或在某些浏览器下不可靠的时候。

event.isImmediatePropagationStopped v1.1+

event.isImmediatePropagationStopped()   ⇒ boolean

如果stopImmediatePropagation()被该事件的实例调用,那么返回true。Zepto在不支持该原生方法的浏览器中实现它,  (例如老版本的Android)。

event.isPropagationStopped v1.1+

event.isPropagationStopped()   ⇒ boolean

如果stopPropagation()被该事件的实例调用,那么返回true。

trigger

trigger(event, [args])   ⇒ self

在对象集合的元素上触发指定的事件。事件可以是一个字符串类型,也可以是一个 通过 定义的事件对象。如果给定args参数,它会作为参数传递给事件函数

triggerHandler

triggerHandler(event, [args])   ⇒ self 像trigger它只在当前元素上触发事件,但不冒泡。 在代码注释里面有详细解释

 

以上均是说明事件api的使用,接下来分析一些具体的实现。先从最常用的on开始说起,先上代码,看看zepto里面on的具体实现。

$.fn.on = function(event, selector, data, callback, one) {        var autoRemove, delegator, $this = this            //如果是{'click':function(){},'touchmove':function(){}}            //此时event是Object        if (event && !isString(event)) {            $.each(event, function(type, fn) {                $this.on(type, selector, data, fn, one)            })            return $this        }        if (!isString(selector) && !isFunction(callback) && callback !== false)            callback = data, data = selector, selector = undefined        if (isFunction(data) || data === false)            callback = data, data = undefined        if (callback === false) callback = returnFalse        return $this.each(function(_, element) {            //如果是一次性事件            if (one) autoRemove = function(e) {                    //移除该事件                    remove(element, e.type, callback)                        //执行回调                    return callback.apply(this, arguments)                }                //事件委托,这里是事件冒泡到element元素上            if (selector) delegator = function(e) {                //事件触发元素e.target的祖先级元素                var evt, match = $(e.target).closest(selector, element).get(0)                    //找到了 并且不是element本身                if (match && match !== element) {                    //创建一个event对象                    evt = $.extend(createProxy(e), {                            currentTarget: match,//匹配到的元素                            liveFired: element//委托的元素                        })                        //(autoRemove || callback)不是一次性事件,就调用callback,                        // [evt].concat(slice.call(arguments, 1))拼接参数数组。                    return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))                }            }            add(element, event, callback, data, selector, delegator || autoRemove)        })    }

看上面代码就知道,在on里面对一次性事件,委托事件都做了相关处理,也对$(XXX).on({'click':function(){},'touchmove':function(){}})这种传递Object的情况做了相应处理。

一次性事件,及时绑定函数只值执行一次,zepto里面简单处理,执行一次就移除该事件绑定,所以最后把我们传入的回调包装了一下 

//如果是一次性事件 if (one) autoRemove = function(e) {      //移除该事件      remove(element, e.type, callback)      //执行回调      return callback.apply(this, arguments) }

如果是委托事件,也是把我们传入的callback进行了包装

if (selector) delegator = function(e) {    //事件触发元素e.target的祖先级元素    var evt, match = $(e.target).closest(selector, element).get(0)        //找到了 并且不是element本身    if (match && match !== element) {        //创建一个event对象        evt = $.extend(createProxy(e), {                currentTarget: match,//匹配到的元素                liveFired: element//委托的元素            })            //(autoRemove || callback)不是一次性事件,就调用callback,            // [evt].concat(slice.call(arguments, 1))拼接参数数组。        return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))    }}

不管是一次性事件,还是事件委托,在on里面只是对我们的callback做了一些包装处理,然后再调用add方法执行最后的绑定,看看add的实现

function add(element, events, fn, data, selector, delegator, capture) {    //取到元素的zid    var id = zid(element),        set = (handlers[id] || (handlers[id] = [])) //元素上已经绑定的所有事件处理函数,如果没有赋值一个新数组    events.split(/\s/).forEach(function(event) {        //如果是绑定dom ready事件        if (event == 'ready') return $(document).ready(fn)            //解析事件类型,返回一个包含事件名称和事件命名空间的对象        var handler = parse(event)            // //保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改        handler.fn = fn        handler.sel = selector            // emulate mouseenter, mouseleave            // 模仿 mouseenter, mouseleave        if (handler.e in hover) fn = function(e) {            /*                          relatedTarget为事件相关对象,只有在mouseover和mouseout事件时才有值                          mouseover时表示的是鼠标移出的那个对象,mouseout时表示的是鼠标移入的那个对象                          当related不存在,表示事件不是mouseover或者mouseout,mouseover时!$.contains(this, related)当相关对象不在事件对象内                          且related !== this相关对象不是事件对象时,表示鼠标已经从事件对象外部移入到了对象本身,这个时间是要执行处理函数的                          当鼠标从事件对象上移入到子节点的时候related就等于this了,且!$.contains(this, related)也不成立,这个时间是不需要执行处理函数的                      */            var related = e.relatedTarget            if (!related || (related !== this && !$.contains(this, related)))                return handler.fn.apply(this, arguments)        }        handler.del = delegator //事件委托        var callback = delegator || fn        handler.proxy = function(e) {                e = compatible(e)                    //这个event对象执行过阻止冒泡方法stopImmediatePropagation,这里直接返回。                if (e.isImmediatePropagationStopped()) return                e.data = data                    //调用之前传入的回调函数                var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))                    //当事件处理函数返回false时,阻止默认操作和冒泡                if (result === false) e.preventDefault(), e.stopPropagation()                return result            }            //设置处理函数的在函数集中的位置,remove的时候要用到        handler.i = set.length            //将函数存入函数集中,引用类型,你懂的,handlers里面也有了        set.push(handler)        if ('addEventListener' in element)        //realEvent(handler.e) 处理事件类型,eventCapture绑定事件类型,是捕获还是冒泡            element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))    })}

里面zid,负责到元素上的一个属性,_zid的值初始从1开始。 

//取element的唯一标示符,如果没有,则设置一个并返回 ,保证-zid的唯一性function zid(element) {    return element._zid || (element._zid = _zid++)}

如果是同时绑定多个事件$(XXX).on('tap click',XXX); 通过events.split(/\s/).forEach进行处理

var handler = parse(event),主要是解析出时间的命名空间,以及绑定事件的类型

function parse(event) {    var parts = ('' + event).split('.')    return {        e: parts[0],        //name space        ns: parts.slice(1).sort().join(' ')    }}

最终指向绑定

if ('addEventListener' in element)//realEvent(handler.e) 处理事件类型,eventCapture绑定事件类型,是捕获还是冒泡    element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))

可以看到绑定的时间处理函数是handler.proxy,这个其实也是把传入的callback进行了再次包装。不难想象,如果我们绑定的是一次性事件或者是委托事件、先是在on里面进行了一次包装,然后到了add里面再做了一次,每次的包装都是有特殊意义的。要完全明白这些东西,还是脱离不了基础!! 闭包、作用域、函数、this等等。on和add的相关处理代码上我进行了详细注释,这里不说了。

和on对应的就是off,off的用法上面已经说了,看具体实现

$.fn.off = function(event, selector, callback) {    var $this = this    if (event && !isString(event)) {        $.each(event, function(type, fn) {            $this.off(type, selector, fn)        })        return $this    }    if (!isString(selector) && !isFunction(callback) && callback !== false)        callback = selector, selector = undefined    if (callback === false) callback = returnFalse    return $this.each(function() {        remove(this, event, callback, selector)    })}

一看off的代码基本上处理处理相关参数,剩下没啥了,最后都聚焦到remove的实现上,所以重点关注remove的实现,关注remove的同时需要知道findHandlers

//删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同addfunction remove(element, events, fn, selector, capture) {    //取到元素的zid    var id = zid(element);    (events || '').split(/\s/).forEach(function(event) {        findHandlers(element, event, fn, selector).forEach(function(handler) {            //删除handlers 对应这个元素(通过zid关联的),对应的索引的callback。            //var a=[1,2,3,4,5]  delete a[0],delete a[3]====>[2,3,5]            delete handlers[id][handler.i]                //移除元素上绑定的事件            if ('removeEventListener' in element)                element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))        })    })}
//查找绑定在元素上的指定类型的事件处理函数集合function findHandlers(element, event, fn, selector) {    event = parse(event)    if (event.ns) var matcher = matcherFor(event.ns)    return (handlers[zid(element)] || []).filter(function(handler) {        //判断事件命名空间是否相同        //注意函数是引用类型的数据zid(handler.fn)的作用是返回handler.fn的标示符,如果没有,则给它添加一个,        //这样如果fn和handler.fn引用的是同一个函数,那么fn上应该也可相同的标示符,        //这里就是通过这一点来判断两个变量是否引用的同一个函数        return handler && (!event.e || handler.e == event.e) &&            (!event.ns || matcher.test(handler.ns)) && (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector)    })}

最后看看trigger和triggerHandler的实现,用法上面已经说过了看实现。两者的差异代码注释里面说了,一个是dispatchEvent(event),一个是直接调用handler.proxy(e)。相当于一个经过了浏览器的相关行为,另一个根本没走浏览器,直接调用了元素上绑定事件的对应回调函数。 

$.fn.trigger = function(event, args) {    event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)    event._args = args    return this.each(function() {        // handle focus(), blur() by calling them directly        if (event.type in focus && typeof this[event.type] == "function") this[event.type]()            // items in the collection might not be DOM elements        else if ('dispatchEvent' in this) this.dispatchEvent(event)        else $(this).triggerHandler(event, args)    })}// triggers event handlers on current element just as if an event occurred,// doesn't trigger an actual event, doesn't bubble//触发元素上绑定的指定类型的事件,但是不冒泡$.fn.triggerHandler = function(event, args) {    var e, result    this.each(function(i, element) {        e = createProxy(isString(event) ? $.Event(event) : event)        e._args = args        e.target = element            //遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation方法,            //那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false            //each里的回调函数指定返回false时,会跳出循环,这样就达到的停止执行回面函数的目的        $.each(findHandlers(element, event.type || event), function(i, handler) {            //直接调用handler.proxy发方法,没有经过浏览器,所以很多浏览器的行为不会发生。            // $("input").triggerHandler('focus');            // 此时input上的focus事件触发,但是input不会获取焦点。因为这里直接取到绑定到该元素对应的focus事件,然后调用            //$("input").trigger('focus');            // 此时input上的focus事件触发,input获取焦点。这里最后会dispatchEvent,会触发浏览器相关行为            result = handler.proxy(e)                //如果这个对象调用了ImmediatePropagationStopped方法            if (e.isImmediatePropagationStopped()) return false        })    })    return result}

 代码具体实现还得自己去折腾,没什么特别复杂的,花点事件肯定能够看懂,看不懂多google就行。zepto 的时间设计比起jquery简单很多很多,所以看懂这个再去啃jqeury吧,jqeury看得头大。最后附上事件处理的全部代码,都是自己加上的注释。

1 ;  2 (function($) {  3     var _zid = 1,  4         undefined,  5         slice = Array.prototype.slice,  6         isFunction = $.isFunction,  7         isString = function(obj) {  8             return typeof obj == 'string'  9         }, 10         handlers = {}, 11         specialEvents = {}, 12         focusinSupported = 'onfocusin' in window, 13         focus = { 14             focus: 'focusin', 15             blur: 'focusout' 16         }, 17         hover = { 18             mouseenter: 'mouseover', 19             mouseleave: 'mouseout' 20         } 21         //特殊事件 22     specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 23  24     //取element的唯一标示符,如果没有,则设置一个并返回 ,保证-zid的唯一性 25     function zid(element) { 26             return element._zid || (element._zid = _zid++) 27         } 28         //查找绑定在元素上的指定类型的事件处理函数集合 29     function findHandlers(element, event, fn, selector) { 30             event = parse(event) 31             if (event.ns) var matcher = matcherFor(event.ns) 32             return (handlers[zid(element)] || []).filter(function(handler) { 33                 //判断事件命名空间是否相同 34                 //注意函数是引用类型的数据zid(handler.fn)的作用是返回handler.fn的标示符,如果没有,则给它添加一个, 35                 //这样如果fn和handler.fn引用的是同一个函数,那么fn上应该也可相同的标示符, 36                 //这里就是通过这一点来判断两个变量是否引用的同一个函数 37                 return handler && (!event.e || handler.e == event.e) && 38                     (!event.ns || matcher.test(handler.ns)) && (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector) 39             }) 40         } 41         //解析事件类型,返回一个包含事件名称和事件命名空间的对象 42     function parse(event) { 43             var parts = ('' + event).split('.') 44             return { 45                 e: parts[0], 46                 //name space 47                 ns: parts.slice(1).sort().join(' ') 48             } 49         } 50         //生成命名空间的正则 51     function matcherFor(ns) { 52             return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 53         } 54         //通过给focus和blur事件设置为捕获来达到事件冒泡的目的 55     function eventCapture(handler, captureSetting) { 56             return handler.del && 57                 (!focusinSupported && (handler.e in focus)) || 58                 !!captureSetting 59         } 60         //修复不支持mouseenter和mouseleave的情况 61     function realEvent(type) { 62         return hover[type] || (focusinSupported && focus[type]) || type 63     } 64  65     function add(element, events, fn, data, selector, delegator, capture) { 66         //取到元素的zid 67         var id = zid(element), 68             set = (handlers[id] || (handlers[id] = [])) //元素上已经绑定的所有事件处理函数,如果没有赋值一个新数组 69  70         events.split(/\s/).forEach(function(event) { 71             //如果是绑定dom ready事件 72             if (event == 'ready') return $(document).ready(fn) 73                 //解析事件类型,返回一个包含事件名称和事件命名空间的对象 74             var handler = parse(event) 75                 // //保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改 76             handler.fn = fn 77             handler.sel = selector 78                 // emulate mouseenter, mouseleave 79                 // 模仿 mouseenter, mouseleave 80             if (handler.e in hover) fn = function(e) { 81                 /* 82                           relatedTarget为事件相关对象,只有在mouseover和mouseout事件时才有值 83                           mouseover时表示的是鼠标移出的那个对象,mouseout时表示的是鼠标移入的那个对象 84                           当related不存在,表示事件不是mouseover或者mouseout,mouseover时!$.contains(this, related)当相关对象不在事件对象内 85                           且related !== this相关对象不是事件对象时,表示鼠标已经从事件对象外部移入到了对象本身,这个时间是要执行处理函数的 86                           当鼠标从事件对象上移入到子节点的时候related就等于this了,且!$.contains(this, related)也不成立,这个时间是不需要执行处理函数的 87                       */ 88                 var related = e.relatedTarget 89                 if (!related || (related !== this && !$.contains(this, related))) 90                     return handler.fn.apply(this, arguments) 91             } 92             handler.del = delegator //事件委托 93             var callback = delegator || fn 94             handler.proxy = function(e) { 95                     e = compatible(e) 96                         //这个event对象执行过阻止冒泡方法stopImmediatePropagation,这里直接返回。 97                     if (e.isImmediatePropagationStopped()) return 98                     e.data = data 99                         //调用之前传入的回调函数100                     var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))101                         //当事件处理函数返回false时,阻止默认操作和冒泡102                     if (result === false) e.preventDefault(), e.stopPropagation()103                     return result104                 }105                 //设置处理函数的在函数集中的位置,remove的时候要用到106             handler.i = set.length107                 //将函数存入函数集中,引用类型,你懂的,handlers里面也有了108             set.push(handler)109             if ('addEventListener' in element)110             //realEvent(handler.e) 处理事件类型,eventCapture绑定事件类型,是捕获还是冒泡111                 element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))112         })113     }114 115     //删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add116     function remove(element, events, fn, selector, capture) {117         //取到元素的zid118         var id = zid(element);119         (events || '').split(/\s/).forEach(function(event) {120             findHandlers(element, event, fn, selector).forEach(function(handler) {121                 //删除handlers 对应这个元素(通过zid关联的),对应的索引的callback。122                 //var a=[1,2,3,4,5]  delete a[0],delete a[3]====>[2,3,5]123                 delete handlers[id][handler.i]124                     //移除元素上绑定的事件125                 if ('removeEventListener' in element)126                     element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))127             })128         })129     }130 131     $.event = {132             add: add,133             remove: remove134         }135         //看到他 就想起了bind136     $.proxy = function(fn, context) {137         var args = (2 in arguments) && slice.call(arguments, 2)138         if (isFunction(fn)) {139             var proxyFn = function() {140                 return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments)141             }142             proxyFn._zid = zid(fn)143             return proxyFn144         } else if (isString(context)) {145             if (args) {146                 args.unshift(fn[context], fn)147                 return $.proxy.apply(null, args)148             } else {149                 return $.proxy(fn[context], fn)150             }151         } else {152             throw new TypeError("expected function")153         }154     }155 156     $.fn.bind = function(event, data, callback) {157         return this.on(event, data, callback)158     }159     $.fn.unbind = function(event, callback) {160         return this.off(event, callback)161     }162 163     $.fn.one = function(event, selector, data, callback) {164         return this.on(event, selector, data, callback, 1)165     }166 167     var returnTrue = function() {168             return true169         },170         returnFalse = function() {171             return false172         },173         ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,174         eventMethods = {175             //是否调用过preventDefault方法176             preventDefault: 'isDefaultPrevented',177             //取消执行其他的事件处理函数并取消事件冒泡.如果同一个事件绑定了多个事件处理函数, 在其中一个事件处理函数中调用此方法后将不会继续调用其他的事件处理函数.178             stopImmediatePropagation: 'isImmediatePropagationStopped', //是否调用过stopImmediatePropagation方法,179             stopPropagation: 'isPropagationStopped' //是否调用过stopPropagation方法180         }181 182         //主要是在event和source做相关的处理183     function compatible(event, source) {184         //存在source 或者 event的isDefaultPrevented不存在185         if (source || !event.isDefaultPrevented) {186             source || (source = event)187             $.each(eventMethods, function(name, predicate) {188                 //source['preventDefault']、source['stopImmediatePropagation']、source['stopPropagation']189                 var sourceMethod = source[name]190                     //event['preventDefault']、event['stopImmediatePropagation']、event['stopPropagation']191                 event[name] = function() {192                         //this['isDefaultPrevented']this['isImmediatePropagationStopped']this['isPropagationStopped']193                         //一旦调用过,event对象相应的值就会发生变化, 之前是returnFalse,现在是returnTrue194                         this[predicate] = returnTrue195                         return sourceMethod && sourceMethod.apply(source, arguments)196                     }197                     //event['isDefaultPrevented']、event['isImmediatePropagationStopped']、event['isPropagationStopped']198                 event[predicate] = returnFalse199             })200 201             if (source.defaultPrevented !== undefined ? source.defaultPrevented :202                 'returnValue' in source ? source.returnValue === false :203                 source.getPreventDefault && source.getPreventDefault())204 205                 event.isDefaultPrevented = returnTrue206         }207         return event208     }209 210     function createProxy(event) {211         var key, proxy = {212             originalEvent: event //保存原始event213         }214         for (key in event)215             //不是需要忽略的216             if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] //复制event属性至proxy217 218         return compatible(proxy, event)219     }220 221     $.fn.delegate = function(selector, event, callback) {222         return this.on(event, selector, callback)223     }224     $.fn.undelegate = function(selector, event, callback) {225         return this.off(event, selector, callback)226     }227     $.fn.live = function(event, callback) {228         //委托到body上229         $(document.body).delegate(this.selector, event, callback)230         return this231     }232     $.fn.die = function(event, callback) {233         $(document.body).undelegate(this.selector, event, callback)234         return this235     }236     $.fn.on = function(event, selector, data, callback, one) {237         var autoRemove, delegator, $this = this238             //如果是{'click':function(){},'touchmove':function(){}}239             //此时event是Object240         if (event && !isString(event)) {241             $.each(event, function(type, fn) {242                 $this.on(type, selector, data, fn, one)243             })244             return $this245         }246 247         if (!isString(selector) && !isFunction(callback) && callback !== false)248             callback = data, data = selector, selector = undefined249 250         if (isFunction(data) || data === false)251             callback = data, data = undefined252 253         if (callback === false) callback = returnFalse254 255         return $this.each(function(_, element) {256             //如果是一次性事件257             if (one) autoRemove = function(e) {258                     //移除该事件259                     remove(element, e.type, callback)260                         //执行回调261                     return callback.apply(this, arguments)262                 }263                 //事件委托,这里是事件冒泡到element元素上264             if (selector) delegator = function(e) {265                 //事件触发元素e.target的祖先级元素266                 var evt, match = $(e.target).closest(selector, element).get(0)267                     //找到了 并且不是element本身268                 if (match && match !== element) {269                     //创建一个event对象270                     evt = $.extend(createProxy(e), {271                             currentTarget: match,//匹配到的元素272                             liveFired: element//委托的元素273                         })274                         //(autoRemove || callback)不是一次性事件,就调用callback,275                         // [evt].concat(slice.call(arguments, 1))拼接参数数组。276                     return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))277                 }278             }279             add(element, event, callback, data, selector, delegator || autoRemove)280         })281     }282     $.fn.off = function(event, selector, callback) {283         var $this = this284         if (event && !isString(event)) {285             $.each(event, function(type, fn) {286                 $this.off(type, selector, fn)287             })288             return $this289         }290 291         if (!isString(selector) && !isFunction(callback) && callback !== false)292             callback = selector, selector = undefined293 294         if (callback === false) callback = returnFalse295             296         return $this.each(function() {297             remove(this, event, callback, selector)298         })299     }300     $.fn.trigger = function(event, args) {301         event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)302         event._args = args303         return this.each(function() {304             // handle focus(), blur() by calling them directly305             if (event.type in focus && typeof this[event.type] == "function") this[event.type]()306                 // items in the collection might not be DOM elements307             else if ('dispatchEvent' in this) this.dispatchEvent(event)308             else $(this).triggerHandler(event, args)309         })310     }311 312     // triggers event handlers on current element just as if an event occurred,313     // doesn't trigger an actual event, doesn't bubble314     //触发元素上绑定的指定类型的事件,但是不冒泡315     $.fn.triggerHandler = function(event, args) {316         var e, result317         this.each(function(i, element) {318             e = createProxy(isString(event) ? $.Event(event) : event)319             e._args = args320             e.target = element321                 //遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation方法,322                 //那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false323                 //each里的回调函数指定返回false时,会跳出循环,这样就达到的停止执行回面函数的目的324             $.each(findHandlers(element, event.type || event), function(i, handler) {325                 //直接调用handler.proxy发方法,没有经过浏览器,所以很多浏览器的行为不会发生。326                 // $("input").triggerHandler('focus');327                 // 此时input上的focus事件触发,但是input不会获取焦点。因为这里直接取到绑定到该元素对应的focus事件,然后调用328                 //$("input").trigger('focus');329                 // 此时input上的focus事件触发,input获取焦点。这里最后会dispatchEvent,会触发浏览器相关行为330                 result = handler.proxy(e)331                     //如果这个对象调用了ImmediatePropagationStopped方法332                 if (e.isImmediatePropagationStopped()) return false333             })334         })335         return result336     }337 338     // shortcut methods for `.bind(event, fn)` for each event type339     ;340     ('focusin focusout focus blur load resize scroll unload click dblclick ' +341         'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave ' +342         'change select keydown keypress keyup error').split(' ').forEach(function(event) {343             $.fn[event] = function(callback) {344                 return (0 in arguments) ?345                     //多个参数346                     this.bind(event, callback) :347                     //没有参数 直接调用348                     this.trigger(event)349             }350         })351         //根据参数创建一个event对象352     $.Event = function(type, props) {353         //当type是个对象时354         if (!isString(type)) props = type, type = props.type355             //创建一个event对象,如果是click,mouseover,mouseout时,创建的是MouseEvent,bubbles为是否冒泡356         var event = document.createEvent(specialEvents[type] || 'Events'),357             bubbles = true358             //确保bubbles的值为true或false,并将props参数的属性扩展到新创建的event对象上359         if (props)360             for (var name in props)(name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])361                 //初始化event对象,type为事件类型,如click,bubbles为是否冒泡,第三个参数表示是否可以用preventDefault方法来取消默认操作362         event.initEvent(type, bubbles, true)363         return compatible(event)364     }365 366 })(Zepto);
zepto event

 本文地址

 

 

转载于:https://www.cnblogs.com/Bond/p/4206105.html

你可能感兴趣的文章
webdriver.py--解说
查看>>
windows 下配置Eclipse che
查看>>
SearchSploit
查看>>
关于C语言中的转义字符
查看>>
用正则表达式从网页里面提取视频地址
查看>>
JAVA线程优先级
查看>>
解决VC几个编译问题的方法——好用
查看>>
SPOJ #11 Factorial
查看>>
City Upgrades
查看>>
“人少也能办大事”---K2 BPM老客户交流会
查看>>
关于七牛进行图片添加文字水印操作小计
查看>>
DataSource数据库的使用
查看>>
CentOS开启samba实现文件共享
查看>>
MSSQL使用sqlbulkcopy批量插入数据
查看>>
证明一个数能被3整除,当且仅当它的各位数的和能被3整除
查看>>
2018秋寒假作业4—PTA编程总结1
查看>>
android自适应屏幕
查看>>
2019-北航面向对象-电梯作业总结
查看>>
SqlHelper
查看>>
初识算法、数据结构
查看>>