读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 相关文章推荐
用 javascript 实现的点击复制代码
Mar 24 Javascript
类似GMAIL的Ajax信息反馈显示
Feb 16 Javascript
根据选择不同的下拉值出现相对应的文本输入框
Aug 01 Javascript
JSON.stringify转换JSON时日期时间不准确的解决方法
Aug 08 Javascript
jQuery中offsetParent()方法用法实例
Jan 19 Javascript
jQuery过滤选择器用法分析
Feb 10 Javascript
js控制多图左右滚动切换效果代码分享
Aug 26 Javascript
canvas时钟效果
Feb 16 Javascript
jQuery dateRangePicker插件使用方法详解
Jul 28 jQuery
Webstorm2016使用技巧(SVN插件使用)
Oct 29 Javascript
微信小程序实现渐入渐出动画效果
Jun 13 Javascript
vue 实现锚点功能操作
Aug 10 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
php Xdebug 调试扩展的安装与使用.
2010/03/13 PHP
脚本吧 - 幻宇工作室用到js,超强推荐expand.js
2006/12/23 Javascript
javascript小数计算出现近似值的解决办法
2010/02/06 Javascript
javascript textContent与innerText的异同分析
2010/10/22 Javascript
Javascript Request获取请求参数如何实现
2012/11/28 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
2013/11/14 Javascript
js螺旋动画效果的具体实例
2013/11/15 Javascript
通过Javascript读取本地Excel文件内容的代码示例
2014/04/08 Javascript
高性能JavaScript 重排与重绘(2)
2015/08/11 Javascript
基于canvas实现的绚丽圆圈效果完整实例
2016/01/26 Javascript
基于javascript实现tab选项卡切换特效调试笔记
2016/03/30 Javascript
网页前端登录js按Enter回车键实现登陆的两种方法
2016/05/10 Javascript
yarn与npm的命令行小结
2016/10/20 Javascript
AngularJS指令与指令之间的交互功能示例
2016/12/14 Javascript
js实现五星评价功能
2017/03/08 Javascript
详解JavaScript数组过滤相同元素的5种方法
2017/05/23 Javascript
关于javascript sort()排序你可能忽略的一点理解
2017/07/18 Javascript
angular $watch 一个变量的变化(实例讲解)
2017/08/02 Javascript
vue删除html内容的标签样式实例
2018/09/13 Javascript
Vue CLI3搭建的项目中路径相关问题的解决
2018/09/17 Javascript
微信小程序从注册账号到上架(图文详解)
2019/07/17 Javascript
Numpy中的mask的使用
2018/07/21 Python
解决pycharm 安装numpy失败的问题
2019/12/05 Python
flask框架url与重定向操作实例详解
2020/01/25 Python
Python实现实时数据采集新型冠状病毒数据实例
2020/02/04 Python
Python批量安装卸载1000个apk的方法
2020/04/10 Python
Python接口测试文件上传实例解析
2020/05/22 Python
轻松制作精彩视频:Animoto
2018/09/19 全球购物
ECOSUSI官网:女式皮革背包
2019/09/27 全球购物
Yummie官方网站:塑身衣和衣柜必需品
2019/10/29 全球购物
.NET程序员的几道面试题
2012/06/01 面试题
shell程序如何生命变量?shell变量是弱变量吗?
2014/11/10 面试题
客户服务经理岗位职责
2014/01/29 职场文书
党支部半年考察意见
2015/06/01 职场文书
初中政教处工作总结
2015/08/12 职场文书
抖音短视频(douyin)去水印工具的实现代码
2021/03/30 Javascript