读jQuery之十一 添加事件核心方法


Posted in Javascript onJuly 31, 2011

这篇看看其源码,这个add定义如下(省略大部分)

add: function( elem, types, handler, data ) { 
if ( elem.nodeType === 3 || elem.nodeType === 8 ) { 
return; 
} 
... 
}

定义了四个参数elem、types、handler和data分别为HTMLElement、事件类型(如click)、事件响应函数、数据。此外,types 可以以空格分开传多种事件("mouseover mouseout")。handler 有时会是一个对象(实现live时)。data 最后会挂在扩充后的event对象上,即作为event的属性。而event会在handler作为第一个参数拿到,这样也就可以在handler拿到data了。
下面详细说明
if ( elem.nodeType === 3 || elem.nodeType === 8 ) { 
return; 
}

文本和注释节点直接返回。
if ( handler === false ) { 
handler = returnFalse; 
} else if ( !handler ) { 
// Fixes bug #7229. Fix recommended by jdalton 
return; 
}

参数handler为false时,将handler赋值为returnFalse,returnFalse为一个函数,如下
function returnFalse() { 
return false; 
}

jQuery通过handler为false来阻止元素默认行为,停止事件冒泡。这个需要结合jQuery.event.handle看。
var handleObjIn, handleObj; 
if ( handler.handler ) { 
handleObjIn = handler; 
handler = handleObjIn.handler; 
} 
// Make sure that the function being executed has a unique ID 
if ( !handler.guid ) { 
handler.guid = jQuery.guid++; 
}

定义变量handleObjIn,handleObj。
handler从字面上看是事件响应(回调)函数,但这里出现handler.handler,让人倍感怪异。即什么时候会将handler当一个JS对象传入呢?
多数时候传的还是Function类型的,看看源码中jQuery.event.add的调用可发现jQuery在实现live的时候会传Object类型。如下
add: function( handleObj ) { 
jQuery.event.add( this, 
liveConvert( handleObj.origType, handleObj.selector ), 
jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
},

这时会把handleObjIn赋值为所传的JS对象,真正的handler 却是handleObjIn.handler。这话有点绕,慢慢体会。
// Make sure that the function being executed has a unique ID 
if ( !handler.guid ) { 
handler.guid = jQuery.guid++; 
}

所传参数handler添加个属性guid,为一个数字,自增的从1开始。即使用jQuery添加事件,会为事件响应函数默认的添加了属性guid。这个guid再删除事件时会用到。
// Init the element's event structure 
var elemData = jQuery._data( elem );

先取elemData,这里使用了前面提到的jQuery._data。第一次为HTMLElement添加事件是elemData是个空对象({})。
// If no elemData is found then we must be trying to bind to one of the 
// banned noData elements 
if ( !elemData ) { 
return; 
}

elemData不存在则直接返回。
var events = elemData.events, 
eventHandle = elemData.handle;

定义events,eventHandle。同样第一次时这两个变量都是undefined。
if ( !events ) { 
elemData.events = events = {}; 
} 
if ( !eventHandle ) { 
elemData.handle = eventHandle = function( e ) { 
// Discard the second event of a jQuery.event.trigger() and 
// when an event is called after a page has unloaded 
return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? 
jQuery.event.handle.apply( eventHandle.elem, arguments ) : 
undefined; 
}; 
}

给elemData.events和elemData.handle赋值。
// Add elem as a property of the handle function 
// This is to prevent a memory leak with non-native events in IE. 
eventHandle.elem = elem;

暂存elem到eventHandle,删除事件注册时会将其置null,避免部分浏览器中内存泄露。
// Handle multiple events separated by a space 
// jQuery(...).bind("mouseover mouseout", fn); 
types = types.split(" ");

将字符串以空格为切割符转成数组。这句使其可以一次添加多个事件,多个事件的handler是相同的。
后面是一个while循环
while ( (type = types[ i++ ]) ) { 
handleObj = handleObjIn ? 
jQuery.extend({}, handleObjIn) : 
{ handler: handler, data: data }; 
... 
}

循环数组,里面依次处理如下
, 取得handleObj
, 处理事件命名空间,以点号(.)来区别。如果type有点号,则具有命名空间,否则没有
, 给handlerObj添加type,guid属性。这些后续删除事件时用到
, 取到handlers,special。多数情况下使用addEventListener/attachEvent来添加事件。从变量special可看出对于特殊的事件如ready,beforeunload及live事件是特殊处理的。 ready 调用的是jQuery.bindReady,而jQuery.bindReady内部调用的仍然是 addEventListener/attachEvent。beforeunload则是使用window.onbeforeunload来添加。live是实现事件代理的,他的处理也是特殊的。
, 最后吧handleObj添加到数组handles中。
jQuery.event.add 的最后一句,解决IE中内存泄露。
// Nullify elem to prevent memory leaks in IE 
elem = null;

jQuery事件管理的数据结构,我做了个图。如下
读jQuery之十一 添加事件核心方法
Javascript 相关文章推荐
jQuery 渐变下拉菜单
Dec 15 Javascript
Javascript Cookie读写删除操作的函数
Mar 02 Javascript
JavaScript中String和StringBuffer的速度之争
Apr 01 Javascript
javascript动态加载二
Aug 22 Javascript
JS实现点击下载的小例子
Jul 10 Javascript
JavaScript中提前声明变量或函数例子
Nov 12 Javascript
javascript实现淘宝幻灯片广告展示效果
Apr 27 Javascript
基于Bootstrap使用jQuery实现输入框组input-group的添加与删除
May 03 Javascript
js控制li的隐藏和显示实例代码
Oct 15 Javascript
vue弹窗组件使用方法
Apr 28 Javascript
jQuery实现表单动态添加数据并提交的方法
Jul 19 jQuery
JS中getElementsByClassName与classList兼容性问题解决方案分析
Aug 07 Javascript
仅Firefox中链接A无法实现模拟点击以触发其默认行为
Jul 31 #Javascript
各浏览器对click方法的支持差异小结
Jul 31 #Javascript
js中将HTMLCollection/NodeList/伪数组转换成数组的代码
Jul 31 #Javascript
对象无length属性时IE6/IE7中无法将其转换成伪数组(ArrayLike)
Jul 31 #Javascript
javascript中length属性的探索
Jul 31 #Javascript
javascript string字符串优化问题
Jul 31 #Javascript
超级有用的13个基于jQuery的内容滚动插件和教程
Jul 31 #Javascript
You might like
dede全站URL静态化改造[070414更正]
2007/04/17 PHP
PHP仿盗链代码
2012/06/03 PHP
php 生成短网址原理及代码
2014/01/23 PHP
php常用的url处理函数总结
2014/11/19 PHP
PHP扩展开发入门教程
2015/02/26 PHP
学习php设计模式 php实现建造者模式
2015/12/07 PHP
IIS 7.5 asp Session超时时间设置方法
2017/04/17 PHP
PHP实现的数据对象映射模式详解
2019/03/20 PHP
js调用打印机打印网页字体总是缩小一号的解决方法
2014/01/24 Javascript
在myeclipse中如何加入jquery代码提示功能
2014/06/03 Javascript
javascript实现自动输出文本(打字特效)
2015/08/27 Javascript
AngularJS表单详解及示例代码
2016/08/17 Javascript
用jQuery实现可输入多选下拉组合框实例代码
2017/01/18 Javascript
js异步编程小技巧详解
2017/08/14 Javascript
详解mpvue小程序中怎么引入iconfont字体图标
2018/10/01 Javascript
Vue过渡效果之CSS过渡详解(结合transition,animation,animate.css)
2020/02/05 Javascript
JS如何寻找数组中心索引过程解析
2020/06/01 Javascript
python获取本机mac地址和ip地址的方法
2015/04/29 Python
python实现用户管理系统
2018/01/10 Python
python最长回文串算法
2018/06/04 Python
使用Django连接Mysql数据库步骤
2019/01/15 Python
selenium+python自动化测试之使用webdriver操作浏览器的方法
2019/01/23 Python
Python3最长回文子串算法示例
2019/03/04 Python
Python之lambda匿名函数及map和filter的用法
2019/03/05 Python
英国打印机墨盒销售网站:Ink Factory
2019/10/07 全球购物
教师党员思想汇报
2014/01/06 职场文书
质量月活动策划方案
2014/03/10 职场文书
冬季施工防火方案
2014/05/17 职场文书
餐饮周年庆活动方案
2014/08/14 职场文书
2014年领导班子专项整治整改方案
2014/09/28 职场文书
承租经营合作者协议书
2014/10/01 职场文书
2014年行政后勤工作总结
2014/12/06 职场文书
蓬莱阁导游词
2015/02/04 职场文书
中标通知书
2015/04/17 职场文书
生产现场禁烟通知
2015/04/23 职场文书
标准发言稿结尾
2019/07/18 职场文书