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)
原文地址:https://www.cnblogs.com/ward/p/6095582.html