前面分析了zepto的事件绑定,接下来分析事件解绑,先看一下zepto中解绑的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) }) }
offf方法前面大致和on方法一样,最主要的是最后return里面的remove函数,在看remove之前,要先分析之前遇到的有关handler的函数,下面是在add方法中,关于handler的。
function add(element, events, fn, data, selector, delegator, capture){ var id = zid(element), set = (handlers[id] || (handlers[id] = [])) events.split(/s/).forEach(function(event){ ... var handler = parse(event) handler.fn = fn handler.sel = selector console.log(handler.e) // emulate mouseenter, mouseleave if (handler.e in hover) fn = function(e){ ... } handler.del = delegator var callback = delegator || fn handler.proxy = function(e){ ... ... } handler.i = set.length set.push(handler) if ('addEventListener' in element) element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) }) }
首先通过zid函数设置一个id,并获得hanlders[id]数组,这里面使用了闭包的功能,在整个事件函数中定义了handlers和_zid两个变量,并在内部函数中采用函数的方式返回,使其不被回收。每个元素都有一个id,该id可以在handlers里面找到自己的事件队列。
var _zid = 1,handlers = {}; function zid(element) { return element._zid || (element._zid = _zid++) }; function add(...){ var id = zid(element), set = (handlers[id] || (handlers[id] = [])); ... };
$(".out").on('click',function(event) { console.log($(this)) event.stopPropagation(); }); $(".in").on('click',function(event) { console.log($(this)) });
接下来,add函数定义了一个handler变量。该变量通过将event作为参数传入parse函数获得。
function parse(event) { var parts = ('' + event).split('.') //返回一个对象,e为事件的type属性,ns为事件的命名空间 return {e: parts[0], ns: parts.slice(1).sort().join(' ')} }
接下来,将事件的一系列相关参数放入handler里面,最后设置handler.i为set的长度(作为handler事件的一个index),并将handler放进set,也就是放进handlers[id]。
也就是说,像上面的例子定义之后,handlers里面是这样的(每一个属性代表一个元素里面的事件队列,如1是对应的数组是div.in的事件队列)。
分析handlers相关之后,就可以看remove函数了:
function remove(element, events, fn, selector, capture){ //获得元素在handlers中的对应id 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)) }) }) }
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)
})
}
首先通过finHandlers函数来查询符合的事件,该函数相对简单,通过filter来返回一个符合条件的事件数组,接下来通过delete语句删除handlers[id]中对应的handler,并使用原生的解绑事件解绑。
那么整个事件队列的分析就结束了,之前在分析$.on函数时,也有一个remove没有分析。
$.fn.on = function(event, selector, data, callback, one){ ... return $this.each(function(_, element){ if (one) autoRemove = function(e){ remove(element, e.type, callback) return callback.apply(this, arguments) } ... add(element, event, callback, data, selector, delegator || autoRemove) }) }
如果传入了one参数,那么就定义一个autoRemove函数,内部调用remove解绑,并返回绑定事件的执行。这样就保证了只执行一次。