• zepto源码学习-04 event


    之前说完$(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){ ... })   ⇒ self
    off({ type: handler, type2: handler2, ... }, [selector])   ⇒ self
    off(type, [selector])   ⇒ self
    off()   ⇒ self
    

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

    on

    on(type, [selector], function(e){ ... })   ⇒ self
    on(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
    

    在对象集合的元素上触发指定的事件。事件可以是一个字符串类型,也可以是一个 通过$.Event 定义的事件对象。如果给定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

    //删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add
    function 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 result
    104                 }
    105                 //设置处理函数的在函数集中的位置,remove的时候要用到
    106             handler.i = set.length
    107                 //将函数存入函数集中,引用类型,你懂的,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     //删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add
    116     function remove(element, events, fn, selector, capture) {
    117         //取到元素的zid
    118         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: remove
    134         }
    135         //看到他 就想起了bind
    136     $.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 proxyFn
    144         } 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 true
    169         },
    170         returnFalse = function() {
    171             return false
    172         },
    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,现在是returnTrue
    194                         this[predicate] = returnTrue
    195                         return sourceMethod && sourceMethod.apply(source, arguments)
    196                     }
    197                     //event['isDefaultPrevented']、event['isImmediatePropagationStopped']、event['isPropagationStopped']
    198                 event[predicate] = returnFalse
    199             })
    200 
    201             if (source.defaultPrevented !== undefined ? source.defaultPrevented :
    202                 'returnValue' in source ? source.returnValue === false :
    203                 source.getPreventDefault && source.getPreventDefault())
    204 
    205                 event.isDefaultPrevented = returnTrue
    206         }
    207         return event
    208     }
    209 
    210     function createProxy(event) {
    211         var key, proxy = {
    212             originalEvent: event //保存原始event
    213         }
    214         for (key in event)
    215             //不是需要忽略的
    216             if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] //复制event属性至proxy
    217 
    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 this
    231     }
    232     $.fn.die = function(event, callback) {
    233         $(document.body).undelegate(this.selector, event, callback)
    234         return this
    235     }
    236     $.fn.on = function(event, selector, data, callback, one) {
    237         var autoRemove, delegator, $this = this
    238             //如果是{'click':function(){},'touchmove':function(){}}
    239             //此时event是Object
    240         if (event && !isString(event)) {
    241             $.each(event, function(type, fn) {
    242                 $this.on(type, selector, data, fn, one)
    243             })
    244             return $this
    245         }
    246 
    247         if (!isString(selector) && !isFunction(callback) && callback !== false)
    248             callback = data, data = selector, selector = undefined
    249 
    250         if (isFunction(data) || data === false)
    251             callback = data, data = undefined
    252 
    253         if (callback === false) callback = returnFalse
    254 
    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 = this
    284         if (event && !isString(event)) {
    285             $.each(event, function(type, fn) {
    286                 $this.off(type, selector, fn)
    287             })
    288             return $this
    289         }
    290 
    291         if (!isString(selector) && !isFunction(callback) && callback !== false)
    292             callback = selector, selector = undefined
    293 
    294         if (callback === false) callback = returnFalse
    295             
    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 = args
    303         return this.each(function() {
    304             // handle focus(), blur() by calling them directly
    305             if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
    306                 // items in the collection might not be DOM elements
    307             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 bubble
    314     //触发元素上绑定的指定类型的事件,但是不冒泡
    315     $.fn.triggerHandler = function(event, args) {
    316         var e, result
    317         this.each(function(i, element) {
    318             e = createProxy(isString(event) ? $.Event(event) : event)
    319             e._args = args
    320             e.target = element
    321                 //遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation方法,
    322                 //那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false
    323                 //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 false
    333             })
    334         })
    335         return result
    336     }
    337 
    338     // shortcut methods for `.bind(event, fn)` for each event type
    339     ;
    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.type
    355             //创建一个event对象,如果是click,mouseover,mouseout时,创建的是MouseEvent,bubbles为是否冒泡
    356         var event = document.createEvent(specialEvents[type] || 'Events'),
    357             bubbles = true
    358             //确保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

     本文地址http://www.cnblogs.com/Bond/p/4206105.html 

     

  • 相关阅读:
    centos配置ADSL拨号 配置阿里云的yum源
    强烈推荐:一个网页转安卓App的工具,可以在线云打包apk
    【html5】input标签capture属性不为人知的秘密
    国庆微信头像在线生成器,头像加国旗小工具
    这是一个玩法简单又充满刺激的安卓App游戏
    微信群二维码突破100人限制和7天不过期的方法
    Xcode9学习笔记84
    Xcode9学习笔记83
    Xcode9学习笔记80
    python 压缩文件.zip
  • 原文地址:https://www.cnblogs.com/Bond/p/4206105.html
Copyright © 2020-2023  润新知