Event 模塊是 Zepto 必備的模塊之一,因爲對 Event Api 不太熟,Event 對象也比較複雜,因此乍一看 Event 模塊的源碼,有點懵,細看下去,其實也不太複雜。javascript
讀Zepto源碼系列文章已經放到了github上,歡迎star: reading-zeptohtml
本文閱讀的源碼爲 zepto1.2.0java
爲何要對 focus
和 blur
事件進行模擬呢?從 MDN 中能夠看到, focus
事件和 blur
事件並不支持事件冒泡。不支持事件冒泡帶來的直接後果是不能進行事件委託,因此須要對 focus
和 blur
事件進行模擬。git
除了 focus
事件和 blur
事件外,現代瀏覽器還支持 focusin
事件和 focusout
事件,他們和 focus
事件及 blur
事件的最主要區別是支持事件冒泡。所以能夠用 focusin
和模擬 focus
事件的冒泡行爲,用 focusout
事件來模擬 blur
事件的冒泡行爲。github
咱們能夠經過如下代碼來肯定這四個事件的執行順序:web
<input id="test" type="text" />
const target = document.getElementById('test') target.addEventListener('focusin', () => {console.log('focusin')}) target.addEventListener('focus', () => {console.log('focus')}) target.addEventListener('blur', () => {console.log('blur')}) target.addEventListener('focusout', () => {console.log('focusout')})
在 chrome59
下, input
聚焦和失焦時,控制檯會打印出以下結果:正則表達式
'focus' 'focusin' 'blur' 'focusout'
能夠看到,在此瀏覽器中,事件的執行順序應該是 focus > focusin > blur > focusout
chrome
關於這幾個事件更詳細的描述,能夠查看:《說說focus /focusin /focusout /blur 事件》segmentfault
關於事件的執行順序,我測試的結果與文章所說的有點不太同樣。感興趣的能夠點擊這個連接測試下http://jsbin.com/nizugazamo/edit?html,js,console,output。不過我以爲執行順序能夠沒必要細究,能夠將 focusin
做爲 focus
事件的冒泡版本。數組
跟 focus
和 blur
同樣,mouseenter
和 mouseleave
也不支持事件的冒泡, 可是 mouseover
和 mouseout
支持事件冒泡,所以,這兩個事件的冒泡處理也能夠分別用 mouseover
和 mouseout
來模擬。
在鼠標事件的 event
對象中,有一個 relatedTarget
的屬性,從 MDN:MouseEvent.relatedTarget 文檔中,能夠看到,mouseover
的 relatedTarget
指向的是移到目標節點上時所離開的節點( exited from
),mouseout
的 relatedTarget
所指向的是離開所在的節點後所進入的節點( entered to
)。
另外 mouseover
事件會隨着鼠標的移動不斷觸發,可是 mouseenter
事件只會在進入節點的那一刻觸發一次。若是鼠標已經在目標節點上,那 mouseover
事件觸發時的 relatedTarget
爲當前節點。
所以,要模擬 mouseenter
或 mouseleave
事件,只須要肯定觸發 mouseover
或 mouseout
事件上的 relatedTarget
不存在,或者 relatedTarget
不爲當前節點,而且不爲當前節點的子節點,避免子節點事件冒泡的影響。
關於 mouseenter
和 mouseleave
的模擬, 謙龍 有篇文章《mouseenter與mouseover爲什麼這般糾纏不清?》寫得很清楚,建議讀一下。
將 Event
模塊簡化後以下:
;(function($){})(Zepto)
其實就是向閉包中傳入 Zepto
對象,而後對 Zepto
對象作一些擴展。
在 Event
模塊中,主要作了以下幾件事:
提供簡潔的API
統一不一樣瀏覽器的 event
對象
事件句柄緩存池,方便手動觸發事件和解綁事件。
事件委託
var _zid = 1 function zid(element) { return element._zid || (element._zid = _zid++) }
獲取參數 element
對象的 _zid
屬性,若是屬性不存在,則全局變量 _zid
增長 1
,做爲 element
的 _zid
的屬性值返回。這個方法用來標記已經綁定過事件的元素,方便查找。
function parse(event) { var parts = ('' + event).split('.') return {e: parts[0], ns: parts.slice(1).sort().join(' ')} }
在 zepto
中,支持事件的命名空間,能夠用 eventType.ns1.ns2...
的形式來給事件添加一個或多個命名空間。
parse
函數用來分解事件名和命名空間。
'' + event
是將 event
變成字符串,再以 .
分割成數組。
返回的對象中,e
爲事件名, ns
爲排序後,以空格相連的命名空間字符串,形如 ns1 ns2 ns3 ...
的形式。
function matcherFor(ns) { return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') }
生成匹配命名空間的表達式,例如,傳進來的參數 ns
爲 ns1 ns2 ns3
,最終生成的正則爲 /(?:^| )ns1.* ?ns2.* ?ns3(?: |$)/
。至於有什麼用,下面立刻講到。
handlers = {} function findHandlers(element, event, fn, selector) { event = parse(event) if (event.ns) var matcher = matcherFor(event.ns) return (handlers[zid(element)] || []).filter(function(handler) { return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector) }) }
查找元素對應的事件句柄。
event = parse(event)
調用 parse
函數,分隔出 event
參數的事件名和命名空間。
if (event.ns) var matcher = matcherFor(event.ns)
若是命名空間存在,則生成匹配該命名空間的正則表達式 matcher
。
return (handlers[zid(element)] || []).filter(function(handler) { ... })
返回的實際上是 handlers[zid(element)]
中符合條件的句柄函數。 handlers
是緩存的句柄容器,用 element
的 _zid
屬性值做爲 key
。
return handler // 條件1 && (!event.e || handler.e == event.e) // 條件2 && (!event.ns || matcher.test(handler.ns)) // 條件3 && (!fn || zid(handler.fn) === zid(fn)) // 條件4 && (!selector || handler.sel == selector) // 條件5
返回的句柄必須知足5個條件:
句柄必須存在
若是 event.e
存在,則句柄的事件名必須與 event
的事件名一致
若是命名空間存在,則句柄的命名空間必需要與事件的命名空間匹配( matcherFor
的做用 )
若是指定匹配的事件句柄爲 fn
,則當前句柄 handler
的 _zid
必須與指定的句柄 fn
相一致
若是指定選擇器 selector
,則當前句柄中的選擇器必須與指定的選擇器一致
從上面的比較能夠看到,緩存的句柄對象的形式以下:
{ fn: '', // 函數 e: '', // 事件名 ns: '', // 命名空間 sel: '', // 選擇器 // 除此以外,其實還有 i: '', // 函數索引 del: '', // 委託函數 proxy: '', // 代理函數 // 後面這幾個屬性會講到 }
focusinSupported = 'onfocusin' in window, focus = { focus: 'focusin', blur: 'focusout' }, hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } function realEvent(type) { return hover[type] || (focusinSupported && focus[type]) || type }
這個函數實際上是將 focus/blur
轉換成 focusin/focusout
,將 mouseenter/mouseleave
轉換成 mouseover/mouseout
事件。
因爲 focusin/focusout
事件瀏覽器支持程度還不是很好,所以要對瀏覽器支持作一個檢測,若是瀏覽器支持,則返回,不然,返回原事件名。
returnTrue = function(){return true}, returnFalse = function(){return false}, eventMethods = { preventDefault: 'isDefaultPrevented', stopImmediatePropagation: 'isImmediatePropagationStopped', stopPropagation: 'isPropagationStopped' } function compatible(event, source) { if (source || !event.isDefaultPrevented) { source || (source = event) $.each(eventMethods, function(name, predicate) { var sourceMethod = source[name] event[name] = function(){ this[predicate] = returnTrue return sourceMethod && sourceMethod.apply(source, arguments) } event[predicate] = returnFalse }) try { event.timeStamp || (event.timeStamp = Date.now()) } catch (ignored) { } if (source.defaultPrevented !== undefined ? source.defaultPrevented : 'returnValue' in source ? source.returnValue === false : source.getPreventDefault && source.getPreventDefault()) event.isDefaultPrevented = returnTrue } return event }
compatible
函數用來修正 event
對象的瀏覽器差別,向 event
對象中添加了 isDefaultPrevented
、isImmediatePropagationStopped
、isPropagationStopped
幾個方法,對不支持 timeStamp
的瀏覽器,向 event
對象中添加 timeStamp
屬性。
if (source || !event.isDefaultPrevented) { source || (source = event) $.each(eventMethods, function(name, predicate) { var sourceMethod = source[name] event[name] = function(){ this[predicate] = returnTrue return sourceMethod && sourceMethod.apply(source, arguments) } event[predicate] = returnFalse })
判斷條件是,原事件對象存在,或者事件 event
的 isDefaultPrevented
不存在時成立。
若是 source
不存在,則將 event
賦值給 source
, 做爲原事件對象。
遍歷 eventMethods
,得到原事件對象的對應方法名 sourceMethod
。
event[name] = function(){ this[predicate] = returnTrue return sourceMethod && sourceMethod.apply(source, arguments) }
改寫 event
對象相應的方法,若是執行對應的方法時,先將事件中方法所對應的新方法賦值爲 returnTrue
函數 ,例如執行 preventDefault
方法時, isDefaultPrevented
方法的返回值爲 true
。
event[predicate] = returnFalse
這是將新添加的屬性,初始化爲 returnFalse
方法
try { event.timeStamp || (event.timeStamp = Date.now()) } catch (ignored) { }
這段向不支持 timeStamp
屬性的瀏覽器中添加 timeStamp
屬性。
if (source.defaultPrevented !== undefined ? source.defaultPrevented : 'returnValue' in source ? source.returnValue === false : source.getPreventDefault && source.getPreventDefault()) event.isDefaultPrevented = returnTrue }
這是對瀏覽器 preventDefault
不一樣實現的兼容。
source.defaultPrevented !== undefined ? source.defaultPrevented : '三元表達式'
若是瀏覽器支持 defaultPrevented
, 則返回 defaultPrevented
的值
'returnValue' in source ? source.returnValue === false : '後一個判斷'
returnValue
默認爲 true
,若是阻止了瀏覽器的默認行爲, returnValue
會變爲 false
。
source.getPreventDefault && source.getPreventDefault()
若是瀏覽器支持 getPreventDefault
方法,則調用 getPreventDefault()
方法獲取是否阻止瀏覽器的默認行爲。
判斷爲 true
的時候,將 isDefaultPrevented
設置爲 returnTrue
方法。
ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/, function createProxy(event) { var key, proxy = { originalEvent: event } for (key in event) if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] return compatible(proxy, event) }
zepto
中,事件觸發的時候,返回給咱們的 event
都不是原生的 event
對象,都是代理對象,這個就是代理對象的建立方法。
ignoreProperties
用來排除 A-Z
開頭,即全部大寫字母開頭的屬性,還有以returnValue
結尾,layerX/layerY
,webkitMovementX/webkitMovementY
結尾的非標準屬性。
for (key in event) if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
遍歷原生事件對象,排除掉不須要的屬性和值爲 undefined
的屬性,將屬性和值複製到代理對象上。
最終返回的是修正後的代理對象
function eventCapture(handler, captureSetting) { return handler.del && (!focusinSupported && (handler.e in focus)) || !!captureSetting }
返回 true
表示在捕獲階段執行事件句柄,不然在冒泡階段執行。
若是存在事件代理,而且事件爲 focus/blur
事件,在瀏覽器不支持 focusin/focusout
事件時,設置爲 true
, 在捕獲階段處理事件,間接達到冒泡的目的。
不然做用自定義的 captureSetting
設置事件執行的時機。
function add(element, events, fn, data, selector, delegator, capture){ var id = zid(element), set = (handlers[id] || (handlers[id] = [])) events.split(/\s/).forEach(function(event){ if (event == 'ready') return $(document).ready(fn) var handler = parse(event) handler.fn = fn handler.sel = selector // emulate mouseenter, mouseleave if (handler.e in hover) fn = function(e){ 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) if (e.isImmediatePropagationStopped()) return e.data = data var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) if (result === false) e.preventDefault(), e.stopPropagation() return result } handler.i = set.length set.push(handler) if ('addEventListener' in element) element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) }) }
add
方法是向元素添加事件及事件響應,參數比較多,先來看看各參數的含義:
element // 事件綁定的元素 events // 須要綁定的事件列表 fn // 事件執行時的句柄 data // 事件執行時,傳遞給事件對象的數據 selector // 事件綁定元素的選擇器 delegator // 事件委託函數 capture // 那個階段執行事件句柄
var id = zid(element), set = (handlers[id] || (handlers[id] = []))
獲取或設置 id
, set
爲事件句柄容器。
events.split(/\s/).forEach(function(event){})
對每一個事件進行處理
if (event == 'ready') return $(document).ready(fn)
若是爲 ready
事件,則調用 ready
方法,停止後續的執行
var handler = parse(event) handler.fn = fn handler.sel = selector // emulate mouseenter, mouseleave if (handler.e in hover) fn = function(e){ 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
上的一些屬性,緩存起來。
這裏主要看對 mouseenter
和 mouseleave
事件的模擬,具體的原理上面已經說過,只有在條件成立的時候纔會執行事件句柄。
handler.proxy = function(e){ e = compatible(e) if (e.isImmediatePropagationStopped()) return e.data = data var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) if (result === false) e.preventDefault(), e.stopPropagation() return result }
事件句柄的代理函數。
e
爲事件執行時的原生 event
對象,所以先調用 compatible
對 e
進行修正。
調用 isImmediatePropagationStopped
方法,看是否已經執行過 stopImmediatePropagation
方法,若是已經執行,則停止後續程序的執行。
再擴展 e
對象,將 data
存到 e
的 data
屬性上。
執行事件句柄,將 e
對象做爲句柄的第一個參數。
若是執行完畢後,顯式返回 false
,則阻止瀏覽器的默認行爲和事件冒泡。
set.push(handler) if ('addEventListener' in element) element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
將句柄存入句柄容器
調用元素的 addEventListener
方法,添加事件,事件的回調函數用的是句柄的代理函數,eventCapture(handler, capture)
來用指定是否在捕獲階段執行。
function remove(element, events, fn, selector, capture){ var id = zid(element) ;(events || '').split(/\s/).forEach(function(event){ findHandlers(element, event, fn, selector).forEach(function(handler){ delete handlers[id][handler.i] if ('removeEventListener' in element) element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) }) }) }
首先獲取指定元素的 _zid
;(events || '').split(/\s/).forEach(function(event){})
遍歷須要刪除的 events
findHandlers(element, event, fn, selector).forEach(function(handler){})
調用 findHandlers
方法,查找 event
下須要刪除的事件句柄
delete handlers[id][handler.i]
刪除句柄容器中對應的事件,在 add
函數中的句柄對象中的 i
屬性就用在這裏了,方便查找須要刪除的句柄。
element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
調用 removeEventListener
方法,刪除對應的事件。
$.event = { add: add, remove: remove }
將 add
方法和 remove
方法暴露出去,應該是方便第三方插件作擴展
$.proxy = function(fn, context) { var args = (2 in arguments) && slice.call(arguments, 2) if (isFunction(fn)) { var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } proxyFn._zid = zid(fn) return proxyFn } else if (isString(context)) { if (args) { args.unshift(fn[context], fn) return $.proxy.apply(null, args) } else { return $.proxy(fn[context], fn) } } else { throw new TypeError("expected function") } }
代理函數,做用有點像 JS 中的 bind
方法,返回的是一個代理後改變執行上下文的函數。
var args = (2 in arguments) && slice.call(arguments, 2)
若是提供超過3個參數,則去除前兩個參數,將後面的參數做爲執行函數 fn
的參數。
if (isFunction(fn)) { var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } proxyFn._zid = zid(fn) return proxyFn }
proxy
的執行函數有兩種傳遞方式,一是在第一個參數直接傳入,二是第一個參數爲上下文對象,執行函數也在上下文對象中一塊兒傳入。
這裏判斷 fn
是否爲函數,即第一種傳參方式,調用 fn
函數的 apply
方法,將上下文對象 context
做爲 apply
的第一個參數,若是 args
存在,則與 fn
的參數合併。
給代理後的函數加上 _zid
屬性,方便函數的查找。
else if (isString(context)) { if (args) { args.unshift(fn[context], fn) return $.proxy.apply(null, args) } else { return $.proxy(fn[context], fn) }
若是函數已經包含在上下文對象中,即第一個參數 fn
爲對象,第二個參數 context
爲字符串,用來指定執行函數的在上下文對象中的屬性名。
if (args) { args.unshift(fn[context], fn) return $.proxy.apply(null, args) }
若是參數存在時,將 fn[context]
,也即執行函數和 fn
,也即上下文對象放入 args
數組的開頭,這樣就將參數修正成跟第一種傳參方式同樣,再調用 $.proxy
函數。這裏調用 apply
方法,是由於不知道參數有多少個,調用 apply
能夠以數組的形式傳入。
若是 args
不存在時,肯定的參數項只有兩個,所以能夠直接調用 $.proxy
方法。
specialEvents={}, specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' $.Event = function(type, props) { if (!isString(type)) props = type, type = props.type var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) event.initEvent(type, bubbles, true) return compatible(event) }
specialEvents
是將鼠標事件修正爲 MouseEvents
,這應該是處理瀏覽器的兼容問題,可能有些瀏覽器中,這些事件的事件類型並非 MouseEvents
。
$.Event
方法用來手動建立特定類型的事件。
參數 type
能夠爲字符串,也能夠爲 event
對象。props
爲擴展 event
對象的對象。
if (!isString(type)) props = type, type = props.type
若是不是字符串,也便是 event
對象時,將 type
賦給 props
,type
爲當前 event
對象中的 type
屬性值。
var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
調用 createEvent
方法,建立對應類型的 event
事件,並將事件冒泡默認設置爲 true
if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
遍歷 props
屬性,若是有指定 bubbles
,則採用指定的冒泡行爲,其餘屬性複製到 event
對象上,實現對 event
對象的擴展。
event.initEvent(type, bubbles, true) return compatible(event)
初始化新建立的事件,並將修正後的事件對象返回。
$.fn.on = function(event, selector, data, callback, one){ var autoRemove, delegator, $this = this 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 (callback === undefined || 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) } if (selector) delegator = function(e){ var evt, match = $(e.target).closest(selector, element).get(0) if (match && match !== element) { evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) } } add(element, event, callback, data, selector, delegator || autoRemove) }) }
on
方法來用給元素綁定事件,最終調用的是 add
方法,前面的一大段邏輯主要是修正參數。
var autoRemove, delegator, $this = this if (event && !isString(event)) { $.each(event, function(type, fn){ $this.on(type, selector, data, fn, one) }) return $this }
autoRemove
表示在執行完事件響應後,自動解綁的函數。
event
能夠爲字符串或者對象,當爲對象時,對象的屬性爲事件類型,屬性值爲句柄。
這段是處理 event
爲對象時的狀況,遍歷對象,獲得事件類型和句柄,而後再次調用 on
方法,繼續修正後續的參數。
if (!isString(selector) && !isFunction(callback) && callback !== false) callback = data, data = selector, selector = undefined if (callback === undefined || data === false) callback = data, data = undefined if (callback === false) callback = returnFalse
先來分析第一個 if
,selector
不爲 string
,callback
不爲函數,而且 callback
不爲 false
時的狀況。
這裏能夠肯定 selector
並無傳遞,由於 selector
不是必傳的參數。
所以這裏將 data
賦給 callback
,selector
賦給 data
,將 selector
設置爲 undefined
,由於 selector
沒有傳遞,所以相應參數的位置都前移了一位。
再來看第二個 if
,若是 callback
( 原來的 data
) 爲 undefined
, data
爲 false
時,表示 selector
沒有傳遞,而且 data
也沒有傳遞,所以將 data
賦給 callback
,將 data
設置爲 undefined
,即將參數再前移一位。
第三個 if
,若是 callback === false
,用 returnFalse
函數代替,若是不用 returnFalse
代替,會報錯。
return $this.each(function(_, element){ add(element, event, callback, data, selector, delegator || autoRemove) })
能夠看到,這裏是遍歷元素集合,爲每一個元素都調用 add
方法,綁定事件。
if (one) autoRemove = function(e){ remove(element, e.type, callback) return callback.apply(this, arguments) }
若是隻調用一次,設置 autoRemove
爲一個函數,這個函數在句柄執行前,調用 remove
方法,將綁定在元素上對應事件解綁。
if (selector) delegator = function(e){ var evt, match = $(e.target).closest(selector, element).get(0) if (match && match !== element) { evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) } }
若是 selector
存在,表示須要作事件代理。
調用 closest
方法,從事件的目標元素 e.target
開始向上查找,返回第一個匹配 selector
的元素。關於 closest
方法,見《讀Zepto源碼之集合元素查找》分析。
若是 match
存在,而且 match
不爲當前元素,則調用 createProxy
方法,爲當前事件對象建立代理對象,再調用 $.extend
方法,爲代理對象擴展 currentTarget
和 liveFired
屬性,將代理元素和觸發事件的元素保存到事件對象中。
最後執行句柄函數,以代理元素 match
做爲句柄的上下文,用代理後的 event
對象 evt
替換掉原句柄函數的第一個參數。
將該函數賦給 delegator
,做爲代理函數傳遞給 add
方法。
$.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) }) }
解綁事件
if (event && !isString(event)) { $.each(event, function(type, fn){ $this.off(type, selector, fn) }) return $this }
這段邏輯與 on
方法中的類似,修正參數,再也不細說。
if (!isString(selector) && !isFunction(callback) && callback !== false) callback = selector, selector = undefined if (callback === false) callback = returnFalse
第一個 if
是處理 selector
參數沒有傳遞的狀況的, selector
位置傳遞的實際上是 callback
。
第二個 if
是判斷若是 callback
爲 false
,將 callback
賦值爲 returnFalse
函數。
return $this.each(function(){ remove(this, event, callback, selector) })
最後遍歷全部元素,調用 remove
函數,爲每一個元素解綁事件。
$.fn.bind = function(event, data, callback){ return this.on(event, data, callback) }
bind
方法內部調用的實際上是 on
方法。
$.fn.unbind = function(event, callback){ return this.off(event, callback) }
unbind
方法內部調用的是 off
方法。
$.fn.one = function(event, selector, data, callback){ return this.on(event, selector, data, callback, 1) }
one
方法內部調用的也是 on
方法,只不過默認傳遞了 one
參數爲 1
,表示綁定的事件只執行一下。
$.fn.delegate = function(selector, event, callback){ return this.on(event, selector, callback) }
事件委託,也是調用 on
方法,只是 selector
必定要傳遞。
$.fn.undelegate = function(selector, event, callback){ return this.off(event, selector, callback) }
取消事件委託,內部調用的是 off
方法,selector
必需要傳遞。
$.fn.live = function(event, callback){ $(document.body).delegate(this.selector, event, callback) return this }
動態建立的節點也能夠響應事件。其實事件綁定在 body
上,而後委託到當前節點上。內部調用的是 delegate
方法。
$.fn.die = function(event, callback){ $(document.body).undelegate(this.selector, event, callback) return this }
將由 live
綁定在 body
上的事件銷燬,內部調用的是 undelegate
方法。
$.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 $.each(findHandlers(element, event.type || event), function(i, handler){ result = handler.proxy(e) if (e.isImmediatePropagationStopped()) return false }) }) return result }
直接觸發事件回調函數。
參數 event
能夠爲事件類型字符串,也能夠爲 event
對象。
e = createProxy(isString(event) ? $.Event(event) : event)
若是 event
爲字符串時,則調用 $.Event
工具函數來初始化一個事件對象,再調用 createProxy
來建立一個 event
代理對象。
$.each(findHandlers(element, event.type || event), function(i, handler){ result = handler.proxy(e) if (e.isImmediatePropagationStopped()) return false })
調用 findHandlers
方法來找出事件的全部句柄,調用 proxy
方法,即真正綁定到事件上的回調函數(參見 add
的解釋),拿到方法返回的結果 result
,並查看 isImmediatePropagationStopped
返回的結果是否爲 true
,若是是,馬上停止後續執行。
若是返回的結果 result
爲 false
,也馬上停止後續執行。
因爲 triggerHandler
直接觸發回調函數,因此事件不會冒泡。
$.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) }) }
手動觸發事件。
event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
event
能夠傳遞事件類型,對象和 event
對象。
若是傳遞的是字符串或者純粹對象,則先調用 $.Event
方法來初始化事件,不然調用 compatible
方法來修正 event
對象,因爲 $.Event
方法在內部其實已經調用過 compatible
方法修正 event
對象了的,因此外部不須要再調用一次。
if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
若是是 focus/blur
方法,則直接調用 this.focus()
或 this.blur()
方法,這兩個方法是瀏覽器原生支持的。
若是 this
爲 DOM
元素,即存在 dispatchEvent
方法,則用 dispatchEvent
來觸發事件,關於 dispatchEvent
,能夠參考 MDN: EventTarget.dispatchEvent()。
不然,直接調用 triggerHandler
方法來觸發事件的回調函數。
因爲 trigger
是經過觸發事件來執行事件句柄的,所以事件會冒泡。
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面