内容简介:为什么要看Zepto的源码,因为公司用的是这个。。。。再看这个源码的过程中,因为对事件类型的不充分,导致学习起来有些费劲,所以在讲这个板块之前先对一些事件进行了解。下面是触发点击事件的代码,我们在inner上添加点击事件,在wrapper添加事件,点击inner都会触发click事件。但这种情况需要我们每次都去点击回调函数才会执行,有没有函数不需要我们手动触发,自动触发呢?
为什么要看Zepto的源码,因为公司用的是这个。。。。
再看这个源码的过程中,因为对事件类型的不充分,导致学习起来有些费劲,所以在讲这个板块之前先对一些事件进行了解。
了解基本event信息
事件分发
下面是触发点击事件的代码,我们在inner上添加点击事件,在wrapper添加事件,点击inner都会触发click事件。但这种情况需要我们每次都去点击回调函数才会执行,有没有函数不需要我们手动触发,自动触发呢?
<div class="wrapper"> wrapper <div class="inner">inner</div> </div> <script> var $inner = document.getElementById('inner'); var $wrapper = document.getElementById('wrapper'); $inner.addEventListener('click', function () { console.log(this.innerHTML); }); $wrapper.addEventListener('click', function () { console.log(this.innerHTML); }); </script> 复制代码
这里用到了一个需要用到一个API:createEvent,具体代码如下:
let event = document.createEvent('Event') event.initEvent('click', true, true) $inner.dispatchEvent(event) 复制代码
这里我们通过createEvent创建了一个事件,并且其后必须马上进行初始化,然后通过dispatchEvent进行事件分发,这样就用js代码进行事件的触发,而不需要我们进行点击才能触发。
事件模拟
在event模块中有这么一段代码
focus = {focus: 'focusin', blur: 'focusout'}, hover = {mouseenter: 'mouseover', mouseleave: 'mouseout'} 复制代码
focus和blur我们都知道,但是为什么要重新隐射focusin和blur事件呢,在mdn中我们可以看到focus和focusin的区别在于focus不支持事件冒泡,如果不支持事件冒泡,那么带来的效果就是不能够进行事件委托。
同样的mouseenter和mouseleave也不支持事件冒泡,但是mouseenter会带来巨大的性能消耗,所以我们常用mouseover进行mouseenter进行事件的模拟。在鼠标事件中,有一个relatedTarget事件,在前面提到因为mouseover支持冒泡,那该如何来模拟mouseenter事件呢。relatedTarget事件属性返回的是和事件的目标节点相关的节点。对于mouseover事件来说,该属性是鼠标指针移到目标节点上所离开的那个节点。对于mouseout事件来说,该属性是离开目标时,鼠标进入的节点。根据上面的描述,我们可以对relatedTarget的值进行判断:如果值不是目标元素,也不是目标元素的子元素,就说明鼠标已经移入目标元素而不是在元素内部移动
核心代码
zid
var _zid = 1 function zid(element) { return element._zid || (element._zid = zid++) } 复制代码
zid主要是用来标记已经绑定时间的元素,这个函数返回元素的_zid,如果没有,那就全局的zid加一,并且赋值给元素的_zid属性
parse
function parse(event) { var parts=('' + event).split('.') return { e: parts[0], ns: parts.slice(1).sort().join(' ') } } 复制代码
parse方法用来分解事件名和命名空间,{e: 事件名, ns: 命名空间},先把event变成字符串进行分割,得到事件名,和命名空间,命名空间可以为s1.s2.s3这种
compatible
这是用来修正event对象中浏览器的差异
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 }) event.timeStamp || (event.timeStamp = Date.now()) if (source.defaultPrevented !== undefined ? source.defaultPrevented : 'returnValue' in source ? source.returnValue === false : source.getPreventDefault && source.getPreventDefault()) event.isDefaultPrevented = returnTrue } return event } 复制代码
具体来看看他的代码
if (source || !event.isDefaultPrevented) { source || (source = event) 复制代码
如果原事件存在,或者事件event的isDefaultPrevented为false或者不存在成立 如果原事件source不存在,就把event赋值给source
$.each(eventMethod, function(name, predicate) { var sourceMethod = source[name] event[name] = function(){ this[predicate] = returnTrue return sourceMethod && sourceMethod.apply(source, arguments) } }) 复制代码
这里是遍历eventMethod,获取原事件对应的方法名sourceMethod。对event事件进行重新赋值,先把方法赋值为returnTrue函数,返回执行原方法的返回值。
event[predicate] = returnFalse 复制代码
新添加的属性初始化为returnFalse。
event.timeStamp || (event.timeStamp = Date.now()) 复制代码
看事件是否支持timeStamp,如果不支持,将Date.now()赋值给timeStamp,最后返回做了兼容性处理的event。
createProxy
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) } 复制代码
这个函数的作用在于生成代理的event,首先在proxy的originalEvent挂载本身,然后遍历event,将event的属性复制到proxy,最后返回对proxy和event做兼容性处理。
add
// element 事件绑定的元素,events绑定的事件列表,fn事件执行时的句柄,data传递给事件对象的数据 // 绑定元素的选择器,delegator事件委托函数,capture哪个阶段执行事件句柄 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方法主要是给元素添加事件和事件响应。
id = zid(element), set = (handlers[id] || (handlers[id] = [])) 复制代码
获取element的id,然后通过id来获取他的句柄容器
events.split(/\s/).forEach(function (event) { if (event == 'ready') return $(document).ready(fn) }) 复制代码
对events进行分解,如果event是ready就直接执行fn
var handler = parse(event) handler.fn = fn handler.sel = selector 复制代码
对event进行事件名和命名空间进行分离,然后将信息挂载到handler上,handler的最终结构是这样的:
{ fn: '', // 函数 e: '', // 事件名 ns: '', // 命名空间 sel: '', // 选择器 i: '', // 函数索引 del: '', // 委托函数 proxy: '' // 代理函数 } 复制代码
继续看下面的
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) } } } 复制代码
这就是我们最先提到的mouseover和mouseenter事件,这里就是对Hover事件进行判断,如果related不存在,或者related不等于目标元素,并且不是目标元素的子元素,就能够完成mouseenter的模拟,然后返回函数处理后的结果。
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进行兼容处理,然后判断是否阻止默认行为,如果是则啥都不做,把data挂载到event对象上,e是事件执行时的event对象,并且使用compatible对e进行修正。调用句柄,并且返回处理结果。
set.push(handler) if ('addEventListener' in element) element.addEventListener(realEvent(hander.e), handler.proxy, eventCapture(handler, capture)) 复制代码
向句柄容器添加句柄,并且给元素添加事件。
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 } 复制代码
如果event不是字符串,可能就是对象或者数组,然后对其遍历,每个都调用on函数。
if (!isString(selector) && !isFunction(callback) && callback !== false) callback = data, data = selector, selector = undefined if (callback === undefined || data === false) callback = data, data = undefined 复制代码
这是针对参数不同的情况进行的操作
return $this.each(function (_, element) { if (one) autoRemove = function (e) { remove(element, e.type, callback) return callback.apply(this, arguments) } }) 复制代码
如果one为true,autoRemove进行的操作是把元素上对应的事件进行解绑,并且调用回调。
if (selector) delegator = function (e) { var evt, match = $(e.target).closet(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) 复制代码
如果selector,就需要做事件代理,调用closet找到最近selector的元素。如果match存在,并且不是当前元素,就调用createProxy(),给当前事件对象创建代理对象,在调用extend方法,为其扩展currentTarget和liveFired属性,将代理元素和触发元素保存到事件对象中。 最后执行句柄函数,match作为上下文,用代理后的event对象evt替换掉原句柄函数的第一个函数。
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; } 复制代码
triggerHandler用于直接执行函数。
this.each(function(i, element) { e = createProxy(isString(event) ? $.Event(event) : event) e._args = args e.target = element 复制代码
遍历元素,然后判断event如果是字符串则使用$.Event创建事件,然后使用createProxy创建代理。
$.each(findHandlers(element, event.type || event), function (i, handler) { result = handler.proxy(e); if (e.isImmediatePropagationStopped()) return false; }) 复制代码
寻找元素上所有的句柄,handler.proxy我们在之前提到过这是真的回调函数,如果有isImmediatePropagationStopped,则终止遍历。
Event
$.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) } 复制代码
简单来说这个部分就是创建事件,初始化事件,然后返回兼容后的event。
以上所述就是小编给大家介绍的《Zepto源码学习Event模块》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- NodeJS Cluster模块源码学习
- NodeJS Events模块源码学习
- 试读angular源码第四章:angular模块及JIT编译模块
- 对公司内部某个模块某个源码审计
- 比特币源码分析:txdb 模块(三)
- 比特币源码分析:txdb 模块(二)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
应用随机过程教程及在算法和智能计算中的随机模型
龚光鲁 / 清华大学出版社 / 2004-3 / 42.00元
应用随机过程教程及在算法和智能计算中的随机模型,ISBN:9787302069485,作者:龚光鲁,钱敏平著一起来看看 《应用随机过程教程及在算法和智能计算中的随机模型》 这本书的介绍吧!