在上一篇的分析中,最后$.on方法返回了一个add方法函数的执行,在这里先看一下其代码:
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 console.log(handler.e) // 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)) }) }
在这里先跳过id以及handler部分(handler.e的值即为事件的type属性),该函数主要作用就是将事件添加进元素的事件队列里。
首先是对events进行分割成多个事件,并通过each函数逐个绑定。先来看一下函数里面的条件判定:
hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
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) }
如果事件的type属性是mouseenter或者mouseleave,那么需要重新定义绑定函数fn,什么是relatedTarget呢?
relatedTarget
属性用于在一个事件中查找另外一个元素。有些事件比如 mouseover
通常侧重处理一个特定的目标,而有些有也可能会涉及到第二目标,比如当目标退出第一目标的 mouseover
事件.
获得relatedTarget元素后,zepto对其进行判断,if (!related || (related !== this && !$.contains(this, related))),假设我们有两个div。
<div class="out"> <div class="in"></div> </div>
我们对div.out定义mouseenter事件后,当我们鼠标从div.in移出来,但鼠标仍然在div.out,那么鼠标自始至终都在div.out中,但还是触发了mouseenter事件,这可能与预期中不同,所以zepto在这里加了判断,如果relatedTarget元素不存在,或者relatedTarget元素不是我们想要触发的元素本身,并且不包含在其中,即返回一个调用原来的fn函数。
接下来就是handler.proxy函数了,该函数的作用就是通过apply调用绑定的事件函数,并返回结果。
最后通过原生addEvent来绑定函数。
最后,总结一下zepto中绑定事件的思路:
1.找到目标元素。 即match = $(e.target).closest(selector, element).get(0),如果没有selector即跳过。
2.事件委托。如果存在selector,即重新生成一个事件,并通过compitable,createProxy对其进行修改,并改变其currentTarget属性,如果不存在selector,即对要绑定的dom执行compitable,添加eventMethod中的方法。
3.事件绑定,当存在selector触发时,实际触发的是$对象中的dom元素,只是通过事件委托使得像在符合selector条件元素中执行一样。
ps:当存在selector时,假设绑定了click事件,但点击的是$对象中的dom元素并且区域不在selector中,同样触发了事件对象,只是上一篇中的delegator函数里,如果找不到selector,那么跳过,不执行我们绑定的函数。$中的dom元素绑定的是handler.proxy函数,handler.proxy返回的是delegator的执行结果,而delegator如果找不到selector,那么就不执行我们绑定的函数。