zeptojs库解读2之事件模块
- del: undefined
- e: "click"
- fn: function (){
- i: 0
- ns: "namespace"
- proxy: function (e) {
- sel: undefined
/*
事件处理部份
*/
;
(function($) {
var $$ = $.zepto.qsa,
// _zid是每个事件绑定函数的标识符
handlers = {}, _zid = 1,
specialEvents = {},
hover = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
} specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' //取element的唯一标示符,如果没有,则设置一个并返回 function zid(element) {
return element._zid || (element._zid = _zid++)
}
//查找绑定在元素上的指定类型的事件处理函数集合 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)) //判断事件命名空间是否相同
//注意函数是引用类型的数据zid(handler.fn)的作用是返回handler.fn的标示符,如果没有,则给它添加一个,
//这样如果fn和handler.fn引用的是同一个函数,那么fn上应该也可相同的标示符,
//这里就是通过这一点来判断两个变量是否引用的同一个函数
&& (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector)
})
}
//解析事件类型,返回一个包含事件名称和事件命名空间的对象 function parse(event) {
var parts = ('' + event).split('.')
return {
e: parts[0],
ns: parts.slice(1).sort().join(' ')
}
}
//生成命名空间的正则 function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}
//遍历events
// events可能是1.["click","keyup"] 2."click keyup"
// fn就是事件处理函数
// iterator就是包装函数,外面再套一层的第三方函数
function eachEvent(events, fn, iterator) {
// 不是字符串,就当做数组处理
if ($.type(events) != "string") $.each(events, iterator)
// 字符串,则以空格为标识符,转数组,再进行枚举处理
else events.split(/\s/).forEach(function(type) {
iterator(type, fn)
})
}
//通过给focus和blur事件设置为捕获来达到事件冒泡的目的 function eventCapture(handler, captureSetting) {
return handler.del &&
(handler.e == 'focus' || handler.e == 'blur') || !! captureSetting
} //修复不支持mouseenter和mouseleave的情况 function realEvent(type) {
return hover[type] || type
} //给元素绑定监听事件,可同时绑定多个事件类型,如['click','mouseover','mouseout'],也可以是'click mouseover mouseout'
// add函数做了三件事
// 1.给添加的事件设置唯一id,以此来索引
// 2.对每个事件进行重新封装,有特殊事件需要修复的修复比如mouseover和mouseout,有委托的触发委托,有代理的触发代理
// 3.进行addEventListener绑定
function add(element, events, fn, selector, getDelegate, capture) {
var id = zid(element),
set = (handlers[id] || (handlers[id] = [])) //元素上已经绑定的所有事件处理函数
// 根据events的内容,进行遍历处理
// event是单个事件名称
eachEvent(events, fn, function(event, fn) {
var handler = parse(event)
//保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改
handler.fn = fn
handler.sel = selector
//如果是 mouseenter, mouseleave,则转换成mouseover和mouseout处理
// 这里的参数handler.e是指 事件名称比如click
if (handler.e in hover) fn = function(e) {
/*
relatedTarget为事件相关对象,只有在mouseover和mouseout事件时才有值
mouseover时表示的是鼠标移出的那个对象,mouseout时表示的是鼠标移入的那个对象
当related不存在,表示事件不是mouseover或者mouseout,mouseover时!$.contains(this, related)当相关对象不在事件对象内
且related !== this相关对象不是事件对象时,表示鼠标已经从事件对象外部移入到了对象本身,这个时间是要执行处理函数的
当鼠标从事件对象上移入到子节点的时候related就等于this了,且!$.contains(this, related)也不成立,这个时间是不需要执行处理函数的
*/
var related = e.relatedTarget
if (!related || (related !== this && !$.contains(this, related)))
return handler.fn.apply(this, arguments)
}
//事件委托
handler.del = getDelegate && getDelegate(fn, event)
var callback = handler.del || fn handler.proxy = function(e) {
// 给下面的trigger触发函数,进行e.data自定义增加数据
var result = callback.apply(element, [e].concat(e.data))
//当事件处理函数返回false时,阻止默认操作和冒泡
if (result === false) e.preventDefault(), e.stopPropagation()
return result
}
//设置处理函数的在函数集中的位置
handler.i = set.length
//将函数存入函数集中
set.push(handler)
element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
}
//删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add
// 通过事件唯一标识符,来移除事件
function remove(element, events, fn, selector, capture) {
var id = zid(element)
eachEvent(events || '', fn, function(event, fn) {
findHandlers(element, event, fn, selector).forEach(function(handler) {
// 首先是移除事件管理对象的引用
delete handlers[id][handler.i]
// 其次是移除事件绑定
element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
})
} $.event = {
add: add,
remove: remove
} //设置代理
$.proxy = function(fn, context) {
if ($.isFunction(fn)) {
//如果fn是函数,则申明一个新的函数并用context作为上下文调用fn
var proxyFn = function() {
return fn.apply(context, arguments)
}
//引用fn标示符
proxyFn._zid = zid(fn)
return proxyFn
} else if (typeof context == 'string') {
return $.proxy(fn[context], fn)
} else {
throw new TypeError("expected function")
}
} $.fn.bind = function(event, callback) {
return this.each(function() {
add(this, event, callback)
})
}
$.fn.unbind = function(event, callback) {
return this.each(function() {
remove(this, event, callback)
})
}
//绑定一次性事件监听函数
$.fn.one = function(event, callback) {
return this.each(function(i, element) {
//添加函数,然后在回调函数里再删除绑定。达到一次性事件的目的
add(this, event, callback, null, function(fn, type) {
return function() {
var result = fn.apply(element, arguments) //这里执行绑定的回调
remove(element, type, fn) //删除上面的绑定
return result
}
})
})
} var returnTrue = function() {
return true
},
returnFalse = function() {
return false
},
ignoreProperties = /^([A-Z]|layer[XY]$)/,
eventMethods = {
preventDefault: 'isDefaultPrevented', //是否调用过preventDefault方法
//取消执行其他的事件处理函数并取消事件冒泡.如果同一个事件绑定了多个事件处理函数, 在其中一个事件处理函数中调用此方法后将不会继续调用其他的事件处理函数.
stopImmediatePropagation: 'isImmediatePropagationStopped', //是否调用过stopImmediatePropagation方法,
stopPropagation: 'isPropagationStopped' //是否调用过stopPropagation方法
}
//创建事件代理 function createProxy(event) {
var key, proxy = {
originalEvent: event
} //保存原始event
for (key in event)
if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] //复制event属性至proxy //将preventDefault,stopImmediatePropagatio,stopPropagation方法定义到proxy上
$.each(eventMethods, function(name, predicate) {
proxy[name] = function() {
this[predicate] = returnTrue
return event[name].apply(event, arguments)
}
proxy[predicate] = returnFalse
})
return proxy
} // emulates the 'defaultPrevented' property for browsers that have none
//event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法. function fix(event) {
if (!('defaultPrevented' in event)) {
event.defaultPrevented = false //初始值false
var prevent = event.preventDefault // 引用默认preventDefault
event.preventDefault = function() { //重写preventDefault
this.defaultPrevented = true
prevent.call(this)
}
}
}
//事件委托
$.fn.delegate = function(selector, event, callback) {
return this.each(function(i, element) {
add(element, event, callback, selector, function(fn) {
return function(e) {
//如果事件对象是element里的元素,取与selector相匹配的最近的父元素
var evt, match = $(e.target).closest(selector, element).get(0)
if (match) {
//evt成了一个拥有preventDefault,stopImmediatePropagatio,stopPropagation方法,currentTarge,liveFiredn属性的对象,另也有e的默认属性
evt = $.extend(createProxy(e), {
currentTarget: match,
liveFired: element
})
return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
}
}
})
})
}
//取消事件委托
$.fn.undelegate = function(selector, event, callback) {
return this.each(function() {
remove(this, event, callback, selector)
})
} $.fn.live = function(event, callback) {
$(document.body).delegate(this.selector, event, callback)
return this
}
$.fn.die = function(event, callback) {
$(document.body).undelegate(this.selector, event, callback)
return this
} //on也有live和事件委托的效果,所以可以只用on来绑定事件
$.fn.on = function(event, selector, callback) {
return !selector || $.isFunction(selector) ?
this.bind(event, selector || callback) : this.delegate(selector, event, callback)
}
$.fn.off = function(event, selector, callback) {
return !selector || $.isFunction(selector) ?
this.unbind(event, selector || callback) : this.undelegate(selector, event, callback)
}
//主动触发事件
// 依据
$.fn.trigger = function(event, data) {
if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
fix(event)
event.data = data
return this.each(function() {
// items in the collection might not be DOM elements
// (todo: possibly support events on plain old objects)
if ('dispatchEvent' in this) this.dispatchEvent(event)
})
} // triggers event handlers on current element just as if an event occurred,
// doesn't trigger an actual event, doesn't bubble
//触发元素上绑定的指定类型的事件,但是不冒泡
$.fn.triggerHandler = function(event, data) {
var e, result
this.each(function(i, element) {
e = createProxy(typeof event == 'string' ? $.Event(event) : event)
e.data = data
e.target = element
//遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation,
//那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false
//注意each里的回调函数指定返回false时,会跳出循环,这样就达到的停止执行回面函数的目的
$.each(findHandlers(element, event.type || event), function(i, handler) {
result = handler.proxy(e)
if (e.isImmediatePropagationStopped()) return false
})
})
return result
} // shortcut methods for `.bind(event, fn)` for each event type
;
('focusin focusout load resize scroll unload click dblclick ' +
'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave ' +
'change select keydown keypress keyup error').split(' ').forEach(function(event) {
$.fn[event] = function(callback) {
return callback ?
//如果有callback回调,则认为它是绑定
this.bind(event, callback) :
//如果没有callback回调,则让它主动触发
this.trigger(event)
}
}) ;
['focus', 'blur'].forEach(function(name) {
$.fn[name] = function(callback) {
if (callback) this.bind(name, callback)
else this.each(function() {
try {
this[name]()
} catch (e) {}
})
return this
}
}) //根据参数创建一个event对象,通过事件模拟的方法
$.Event = function(type, props) {
//当type是个对象时
if (typeof type != 'string') props = type, type = props.type
//创建一个event对象,如果是click,mouseover,mouseout时,创建的是MouseEvent,bubbles为是否冒泡
var event = document.createEvent(specialEvents[type] || 'Events'),
bubbles = true
//确保bubbles的值为true或false,并将props参数的属性扩展到新创建的event对象上
if (props)
for (var name in props)(name == 'bubbles') ? (bubbles = !! props[name]) : (event[name] = props[name])
//初始化event对象,type为事件类型,如click,bubbles为是否冒泡,第三个参数表示是否可以用preventDefault方法来取消默认操作
event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
//添加isDefaultPrevented方法,event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.
event.isDefaultPrevented = function() {
return this.defaultPrevented
}
return event
} })(Zepto)
//-----------------------20140103
handlers是一个对象,作用是管理zepto的事件。
handlers的key是一个唯一数字,这个唯一数字又对应一个html元素。
表现为{1:[{del: undefined, e: "click",fn: function (){},i: 0,ns: "namespace",proxy: function (e) {},sel: undefined},{del: undefined, e: "click",fn: function (){},i: 1,ns: "namespace",proxy: function (e) {},sel: undefined}], 2:[], 3:[]}。
通过key,可以很容易把一个html元素绑定的所有事件,都解除绑定。
通过ns命名空间,我们可以触发跨类型的事件,比如click和mousedown同时触发。
通过i可以快速找到事件所在数组的位置,可以快速定位。
fn就是事件处理函数。
del是委托封装了fn,用于事件委托。
$.fn.delegate = function(selector, event, callback) {
return this.each(function(i, element) {
add(element, event, callback, selector, function(fn) {
return function(e) {
//如果事件对象是element里的元素,取与selector相匹配的
var evt, match = $(e.target).closest(selector, element).get(0);
if (match) {
//evt成了一个拥有preventDefault,stopImmediatePropagatio,stopPropagation方法,currentTarge,liveFiredn属性的对象,另也有e的默认属性
evt = $.extend(createProxy(e), {
currentTarget: match,
liveFired: element
})
return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
}
}
})
})
}
到时候执行的回调函数被$.fn.delegate封装过了。当你下次触发事件时,只冒泡到你给定的html元素为止,再判断触发事件来源,然后调用相关事件处理函数。
proxy是封装了fn(或者被委托封装过的fn),可以知道该事件要不要冒泡,阻止默认操作。
sel是zepto对象。
zeptojs库解读2之事件模块的更多相关文章
- zeptojs库解读3之ajax模块
对于ajax,三步骤,第一,创建xhr对象:第二,发送请求:第三,处理响应. 但在编写过程中,实际中会碰到以下问题, 1.超时 2.跨域 3.后退 解决方法: 1.超时 设置定时器,规定的时间内未返回 ...
- zeptojs库解读1之整体框架
首先看的是整体框架, // zepto骨骼,这个函数的作用使得Zepto(slector, context)使用很多$.fn里面的方法 var Zepto = (function(){ // zept ...
- Backbone源码解读(一)事件模块
Backbone源码浅读: 前言: Backbone是早起的js前端MV*框架之一,是一个依赖于underscore和jquery的轻量级框架,虽然underscore中基于字符串拼接的模板引擎相比如 ...
- 深入理解nodejs的异步IO与事件模块机制
node为什么要使用异步I/O 异步I/O的技术方案:轮询技术 node的异步I/O nodejs事件环 一.node为什么要使用异步I/O 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...
- 自己实现一个javascript事件模块
nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...
- Backbone事件模块及其用法
事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...
- nodejs事件模块
nodejs 事件模块 events 只有一个对象 EventEmitter . var EventEmitter = require('events').EventEmitter;var life ...
- jQuery源代码学习之九—jQuery事件模块
jQuery事件系统并没有将事件坚挺函数直接绑定在DOM元素上,而是基于事件缓存模块来管理监听函数的. 二.jQuery事件模块的代码结构 //定义了一些正则 // // //jQuery事件对象 j ...
- Backbone事件模块源码分析
事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...
随机推荐
- [py][mx]django-解决注册用户已存在,激活链接判断
注册时候,如果用户已存在,则提示错误 激活用户时候,如果激活链接失效,则提示用户. class RegisterView(View): def get(self, request): register ...
- JS在不同js文件中互相调用
例如有这样一个html,里面有一个按钮,当按下时调用b.js文件中的方法b().而b()中又要调用a.js文件中的方法a().若要实现这个功能,必须注意,将要引入的Js文件代码放在</body& ...
- jenkins maven testng selenium自动化持续集成
准备环境 首先我们新建一个maven的工程,并且在pom.xml中配置好我们依赖的一些jar包 <dependencies> <dependency> <groupId& ...
- bug管理工具为开发者工作带来哪些改变?
BUG管理工具的主要功能是对软件开发测试过程中出现的BUG进行跟踪管理,提高开发者的工作效率与工作质量. 在实际工作中,如果没有bug管理工具的帮助,就可能会出现如下一系列的影响: 1.软件测试人员将 ...
- 谷歌插件--Advanced REST client
早上在测试调用服务去获取数据的时候,因为自己的单元测试不是很熟悉,问了同事,同事给我介绍了一个插件Advanced REST client,这个可以在谷歌的“扩展与应用”中找打,使用 安装之后会提示要 ...
- ios 透过上层视图点击相应下方视图的点击事件
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ UIView *hitView = [super hitTest:point ...
- mysql合并 两个count语句一次性输出结果的方法
mysql合并 两个count语句一次性输出结果的方法 需求场景:经常要查看有两个表统计数,用SELECT COUNT(*) FROM hotcontents,SELECT COUNT(*) FROM ...
- QEvent postEvent/sendEvent
可以自訂事件類型,最簡單的方式,是透過QEvent::Type指定事件類型的常數值,在建構QCustomEvent時作為建構引數並透過postEvent()傳送事件,例如: const QEvent: ...
- .NET,ASP.NET,ASP.NET MVC 之间的区别
https://www.cnblogs.com/wwym/p/5555772.html
- 异常和TCP通讯
第七章 异常处理 * 异常处理机制中的try-catch * 语法: * try{ * 代码片段 * }catch(XXXException e){ * 当try中的代码片段出现了XXXExcepti ...