zoukankan      html  css  js  c++  java
  • zepto 事件模块源码分析

      1 //     Zepto.js
      2 //     (c) 2010-2016 Thomas Fuchs
      3 //     Zepto.js may be freely distributed under the MIT license.
      4 
      5 ;(function($){
      6   //_zid:element的唯一标识
      7   var _zid = 1, undefined,   
      8       slice = Array.prototype.slice,
      9       isFunction = $.isFunction,
     10       isString = function(obj){ return typeof obj == 'string' },
     11       handlers = {},
     12       specialEvents={},
     13       //focusin focusout不是所有浏览器都支持的。谷歌支持,IE从10开始支持,火狐一直不支持
     14       //https://developer.mozilla.org/en-US/docs/Web/Events 此处可以看出focus blur不支持冒泡.在支持的浏览器中,顺序如下focus>>focusin>>blur>>focusout
     15       focusinSupported = 'onfocusin' in window,
     16       focus = { focus: 'focusin', blur: 'focusout' },
     17       //https://developer.mozilla.org/en-US/docs/Web/Events/mouseenter mouseenter不冒泡 mouseover冒泡。这就会导致父节点监听mouseover事件,其子节点有mouseover时,父节点一回收到mouseover事件
     18       hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
     19 
     20   specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' //一般事件用Events
     21 
     22   function zid(element) {
     23     return element._zid || (element._zid = _zid++)
     24   }
     25   
     26   /*
     27   查找一个预算绑定的函数 事件类型相同,命名空间相同 选择器相同
     28   输入节点,事件类型  
     29   输出处理函数
     30   */
     31   function findHandlers(element, event, fn, selector) {
     32     event = parse(event)
     33     if (event.ns) var matcher = matcherFor(event.ns)
     34     return (handlers[zid(element)] || []).filter(function(handler) {
     35       return handler
     36         && (!event.e  || handler.e == event.e)
     37         && (!event.ns || matcher.test(handler.ns))
     38         && (!fn       || zid(handler.fn) === zid(fn))
     39         && (!selector || handler.sel == selector)
     40     })
     41   }
     42   
     43   //解析事件类型、命名空间
     44   function parse(event) {
     45     var parts = ('' + event).split('.')
     46     return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
     47   }
     48   function matcherFor(ns) {
     49     return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
     50   }
     51 
     52   //对于不冒泡的事件要用捕获的方法达到冒泡的目的
     53   function eventCapture(handler, captureSetting) {
     54     return handler.del &&
     55       (!focusinSupported && (handler.e in focus)) ||
     56       !!captureSetting
     57   }
     58 
     59   //将不可冒泡的事件用可冒泡的事件代替
     60   function realEvent(type) {
     61     return hover[type] || (focusinSupported && focus[type]) || type
     62   }
     63 
     64   //核心函数
     65   function add(element, events, fn, data, selector, delegator, capture){
     66     //set是该节点绑定的监听函数集合
     67     var id = zid(element), set = (handlers[id] || (handlers[id] = []))
     68     events.split(/s/).forEach(function(event){
     69       if (event == 'ready') return $(document).ready(fn)
     70       var handler   = parse(event)
     71       handler.fn    = fn
     72       handler.sel   = selector
     73       // emulate mouseenter, mouseleave
     74       
     75       if (handler.e in hover) fn = function(e){
     76         /*
     77           event有:target:事件发生的节点  currentTarget:监听器所在的节点 relatedTarget:只对mouseover mouseenter mouseout一类事件有效。进入一个节点,被进入的是target,从哪进入的是relatedTarget
     78           下面的判断的意思是:若不是内部移动,则执行函数。可以避免收到多次mouseenter、mouseleave事件
     79         */
     80         var related = e.relatedTarget
     81         if (!related || (related !== this && !$.contains(this, related)))
     82           return handler.fn.apply(this, arguments)
     83       }
     84       handler.del   = delegator
     85       var callback  = delegator || fn
     86       //事件处理函数 加一层代理 方便删除,并加入preventDefault() stopPropagation()
     87       handler.proxy = function(e){
     88         e = compatible(e)
     89         if (e.isImmediatePropagationStopped()) return  //若事件某一步已执行了stopImmediatePropagationStop(),则回调函数不再执行(从源节点到各个接收到冒泡事件的父节点都是同一个事件)
     90         e.data = data
     91         var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
     92         if (result === false) e.preventDefault(), e.stopPropagation() //回调函数 返回false,则不再冒泡或执行默认事件
     93         return result
     94       }
     95       handler.i = set.length
     96       set.push(handler)
     97       if ('addEventListener' in element)
     98         element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) //eventCapture保证绑定在父节点上的监听函数可以收到事件,避免不冒泡的事件导致父节点监听不到的情况
     99     })
    100   }
    101   function remove(element, events, fn, selector, capture){
    102     var id = zid(element)
    103     ;(events || '').split(/s/).forEach(function(event){
    104       findHandlers(element, event, fn, selector).forEach(function(handler){
    105         delete handlers[id][handler.i]
    106       if ('removeEventListener' in element)
    107         element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
    108       })
    109     })
    110   }
    111 
    112   $.event = { add: add, remove: remove }
    113 
    114   $.proxy = function(fn, context) {
    115     var args = (2 in arguments) && slice.call(arguments, 2)
    116     if (isFunction(fn)) {
    117       var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }
    118       proxyFn._zid = zid(fn)
    119       return proxyFn
    120     } else if (isString(context)) {
    121       if (args) {
    122         args.unshift(fn[context], fn)
    123         return $.proxy.apply(null, args)
    124       } else {
    125         return $.proxy(fn[context], fn)
    126       }
    127     } else {
    128       throw new TypeError("expected function")
    129     }
    130   }
    131 
    132   $.fn.bind = function(event, data, callback){
    133     return this.on(event, data, callback)
    134   }
    135   $.fn.unbind = function(event, callback){
    136     return this.off(event, callback)
    137   }
    138   $.fn.one = function(event, selector, data, callback){
    139     return this.on(event, selector, data, callback, 1)
    140   }
    141 
    142   var returnTrue = function(){return true},
    143       returnFalse = function(){return false},
    144       ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,
    145       eventMethods = {
    146         preventDefault: 'isDefaultPrevented',
    147         stopImmediatePropagation: 'isImmediatePropagationStopped',
    148         stopPropagation: 'isPropagationStopped'
    149       }
    150 
    151   function compatible(event, source) {
    152     if (source || !event.isDefaultPrevented) {
    153       source || (source = event)
    154 
    155       $.each(eventMethods, function(name, predicate) {
    156         var sourceMethod = source[name]
    157         event[name] = function(){
    158           this[predicate] = returnTrue
    159           return sourceMethod && sourceMethod.apply(source, arguments)
    160         }
    161         event[predicate] = returnFalse
    162       })
    163 
    164       event.timeStamp || (event.timeStamp = Date.now())
    165 
    166       if (source.defaultPrevented !== undefined ? source.defaultPrevented :
    167           'returnValue' in source ? source.returnValue === false :
    168           source.getPreventDefault && source.getPreventDefault())
    169         event.isDefaultPrevented = returnTrue
    170     }
    171     return event
    172   }
    173 
    174   function createProxy(event) {
    175     var key, proxy = { originalEvent: event }
    176     for (key in event)
    177       if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
    178 
    179     return compatible(proxy, event)
    180   }
    181 
    182   $.fn.delegate = function(selector, event, callback){
    183     return this.on(event, selector, callback)
    184   }
    185   $.fn.undelegate = function(selector, event, callback){
    186     return this.off(event, selector, callback)
    187   }
    188 
    189   $.fn.live = function(event, callback){
    190     $(document.body).delegate(this.selector, event, callback)
    191     return this
    192   }
    193   $.fn.die = function(event, callback){
    194     $(document.body).undelegate(this.selector, event, callback)
    195     return this
    196   }
    197 
    198   $.fn.on = function(event, selector, data, callback, one){
    199     var autoRemove, delegator, $this = this
    200     if (event && !isString(event)) { //处理批量绑定事件的情况
    201       $.each(event, function(type, fn){
    202         $this.on(type, selector, data, fn, one)
    203       })
    204       return $this
    205     }
    206 
    207     /*
    208     处理绑定的各种情况,最全的是5个参数,最简单的$el.bind('click', callback)
    209     */
    210     if (!isString(selector) && !isFunction(callback) && callback !== false)
    211       callback = data, data = selector, selector = undefined //这是只有三个参数的情况,1、event-type 2、data 3、callback。类似$el.bind('click', data, callback)
    212     if (callback === undefined || data === false)
    213       callback = data, data = undefined //若第三个参数还是未定义,则认为是最简单的情况$el.bind('click', callback)
    214 
    215     if (callback === false) callback = returnFalse //若第二个参数仍未定义,则认为是$el.bind('click')
    216 
    217     return $this.each(function(_, element){
    218       if (one) autoRemove = function(e){ //若one有效值,这个在第一遍调用后移除监听事件
    219         remove(element, e.type, callback)
    220         return callback.apply(this, arguments)
    221       }
    222 
    223       //若绑定事件使用了选择器,则使用代理函数
    224       if (selector) delegator = function(e){
    225         //closest是一个很重要的方法 与parents类似,但只会返回最近的节点
    226         var evt, match = $(e.target).closest(selector, element).get(0)
    227         //此处 接收到event后 若检测不匹配 则代理函数为空
    228         if (match && match !== element) {
    229           evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
    230           return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
    231         }
    232       }
    233 
    234       add(element, event, callback, data, selector, delegator || autoRemove)
    235     })
    236   }
    237   $.fn.off = function(event, selector, callback){
    238     var $this = this
    239     if (event && !isString(event)) {
    240       $.each(event, function(type, fn){
    241         $this.off(type, selector, fn)
    242       })
    243       return $this
    244     }
    245 
    246     if (!isString(selector) && !isFunction(callback) && callback !== false)
    247       callback = selector, selector = undefined
    248 
    249     if (callback === false) callback = returnFalse
    250 
    251     return $this.each(function(){
    252       remove(this, event, callback, selector)
    253     })
    254   }
    255 
    256   //一个相对独立的函数,提供给外部使用
    257   $.fn.trigger = function(event, args){
    258     event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
    259     event._args = args
    260     return this.each(function(){
    261       // handle focus(), blur() by calling them directly
    262       if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
    263       // items in the collection might not be DOM elements
    264       else if ('dispatchEvent' in this) this.dispatchEvent(event)  //创建一个事件,dispatchEvent一下触发事件,就是这么简单
    265       else $(this).triggerHandler(event, args)
    266     })
    267   }
    268 
    269   // triggers event handlers on current element just as if an event occurred,
    270   // doesn't trigger an actual event, doesn't bubble
    271   $.fn.triggerHandler = function(event, args){
    272     var e, result
    273     this.each(function(i, element){
    274       e = createProxy(isString(event) ? $.Event(event) : event)
    275       e._args = args
    276       e.target = element
    277       $.each(findHandlers(element, event.type || event), function(i, handler){
    278         result = handler.proxy(e)
    279         if (e.isImmediatePropagationStopped()) return false
    280       })
    281     })
    282     return result
    283   }
    284 
    285   // shortcut methods for `.bind(event, fn)` for each event type
    286   ;('focusin focusout focus blur load resize scroll unload click dblclick '+
    287   'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
    288   'change select keydown keypress keyup error').split(' ').forEach(function(event) {
    289     $.fn[event] = function(callback) {
    290       return (0 in arguments) ?
    291         this.bind(event, callback) :
    292         this.trigger(event) //向外部提供$.click(callback())这种函数调用方式。另外,参数为空就触发对应事件
    293     }
    294   })
    295 
    296   //创建事件的基本方法 MouseEvents || Events
    297   $.Event = function(type, props) {
    298     if (!isString(type)) props = type, type = props.type
    299     var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
    300     if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
    301     event.initEvent(type, bubbles, true)
    302     return compatible(event)
    303   }
    304 
    305 })(Zepto)
  • 相关阅读:
    前进的道路,不要往后看——记得24生日
    【剑指offer】:Q44:直扑克
    手游client思考框架
    Craig可能是个冲浪爱好者
    VMware vSphere服务器虚拟化实验六 vCenter Server 添加储存
    OSX: 使用命令行对FileVault2分区恢复
    让工程师爱上CMMI,实现管理于无形 --- 中标软件CMMI L5之路 (1/2)
    让工程师爱上CMM,实现管理于无形 --- 中标软件CMMI L5之路 (2/2)
    HTML+CSS实例——漂亮的查询部件(一)
    jbpm部署流程定义到MySql报乱码解决方案
  • 原文地址:https://www.cnblogs.com/ward/p/6095582.html
Copyright © 2011-2022 走看看