Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较庞杂,所以乍一看 Event 模块的源码,有点懵,细看下去,实在也不太庞杂。
读Zepto源码系列文章已放到了github上,迎接star: reading-zepto
源码版本
本文浏览的源码为 zepto1.2.0
预备学问
focus/blur 的事宜模仿
为什么要对 focus
和 blur
事宜举行模仿呢?从 MDN 中能够看到, focus
事宜和 blur
事宜并不支撑事宜冒泡。不支撑事宜冒泡带来的直接效果是不能举行事宜托付,所以需要对 focus
和 blur
事宜举行模仿。
除了 focus
事宜和 blur
事宜外,当代浏览器还支撑 focusin
事宜和 focusout
事宜,他们和 focus
事宜及 blur
事宜的最重要区别是支撑事宜冒泡。因而能够用 focusin
和模仿 focus
事宜的冒泡行动,用 focusout
事宜来模仿 blur
事宜的冒泡行动。
我们能够经由过程以下代码来肯定这四个事宜的实行递次:
<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
关于这几个事宜更细致的形貌,能够检察:《说说focus /focusin /focusout /blur 事宜》
关于事宜的实行递次,我测试的效果与文章所说的有点不太一样。感兴趣的能够点击这个链接测试下http://jsbin.com/nizugazamo/edit?html,js,console,output。不过我认为实行递次能够没必要细究,能够将 focusin
作为 focus
事宜的冒泡版本。
mouseenter/mouseleave 的事宜模仿
跟 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 模块的中心
将 Event
模块简化后以下:
;(function($){})(Zepto)
实在就是向闭包中传入 Zepto
对象,然后对 Zepto
对象做一些扩大。
在 Event
模块中,重要做了以下几件事:
供应简约的API
一致差别浏览器的
event
对象事宜句柄缓存池,轻易手动触发事宜息争绑事宜。
事宜托付
内部要领
zid
var _zid = 1
function zid(element) {
return element._zid || (element._zid = _zid++)
}
猎取参数 element
对象的 _zid
属性,假如属性不存在,则全局变量 _zid
增添 1
,作为 element
的 _zid
的属性值返回。这个要领用来标记已绑定过事宜的元素,轻易查找。
parse
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 ...
的情势。
matcherFor
function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}
天生婚配定名空间的表达式,比方,传进来的参数 ns
为 ns1 ns2 ns3
,终究天生的正则为 /(?:^| )ns1.* ?ns2.* ?ns3(?: |$)/
。至于有什么用,下面立时讲到。
findHandlers,查找缓存的句柄
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: '', // 代办函数
// 背面这几个属性会讲到
}
realEvent,返回对应的冒泡事宜
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
事宜浏览器支撑水平还不是很好,因而要对浏览器支撑做一个检测,假如浏览器支撑,则返回,不然,返回原事宜名。
compatible,修改event对象
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
要领。
createProxy,建立代办对象
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
的属性,将属性和值复制到代办对象上。
终究返回的是修改后的代办对象
eventCapture
function eventCapture(handler, captureSetting) {
return handler.del &&
(!focusinSupported && (handler.e in focus)) ||
!!captureSetting
}
返回 true
示意在捕捉阶段实行事宜句柄,不然在冒泡阶段实行。
假如存在事宜代办,而且事宜为 focus/blur
事宜,在浏览器不支撑 focusin/focusout
事宜时,设置为 true
, 在捕捉阶段处置惩罚事宜,间接到达冒泡的目标。
不然作用自定义的 captureSetting
设置事宜实行的机遇。
add,Event 模块的中心要领
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)
来用指定是不是在捕捉阶段实行。
remove,删除事宜
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
$.event = { add: add, remove: remove }
将 add
要领和 remove
要领暴露出去,应该是轻易第三方插件做扩大
$.proxy
$.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
要领。
$.Event
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)
初始化新建立的事宜,并将修改后的事宜对象返回。
要领
.on()
$.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
要领。
.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)
})
}
解绑事宜
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
函数,为每一个元素解绑事宜。
.bind()
$.fn.bind = function(event, data, callback){
return this.on(event, data, callback)
}
bind
要领内部挪用的实际上是 on
要领。
.unbind()
$.fn.unbind = function(event, callback){
return this.off(event, callback)
}
unbind
要领内部挪用的是 off
要领。
.one()
$.fn.one = function(event, selector, data, callback){
return this.on(event, selector, data, callback, 1)
}
one
要领内部挪用的也是 on
要领,只不过默许通报了 one
参数为 1
,示意绑定的事宜只实行一下。
.delegate()
$.fn.delegate = function(selector, event, callback){
return this.on(event, selector, callback)
}
事宜托付,也是挪用 on
要领,只是 selector
一定要通报。
.undelegate()
$.fn.undelegate = function(selector, event, callback){
return this.off(event, selector, callback)
}
作废事宜托付,内部挪用的是 off
要领,selector
必需要通报。
.live()
$.fn.live = function(event, callback){
$(document.body).delegate(this.selector, event, callback)
return this
}
动态建立的节点也能够相应事宜。实在事宜绑定在 body
上,然后托付到当前节点上。内部挪用的是 delegate
要领。
.die()
$.fn.die = function(event, callback){
$(document.body).undelegate(this.selector, event, callback)
return this
}
将由 live
绑定在 body
上的事宜烧毁,内部挪用的是 undelegate
要领。
.triggerHandler()
$.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
直接触发还调函数,所以事宜不会冒泡。
.trigger()
$.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
是经由过程触发事宜来实行事宜句柄的,因而事宜会冒泡。
系列文章
参考
License
末了,一切文章都邑同步发送到微信民众号上,迎接关注,迎接提意见:
作者:对角另一面