读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 事件流和事件绑定
Jul 16 Javascript
javascript 面向对象编程基础 多态
Aug 21 Javascript
silverlight线程与基于事件驱动javascript引擎(实现轨迹回放功能)
Aug 09 Javascript
Jquery插件编写简明教程
Mar 25 Javascript
JQuery实现简单的图片滑动切换特效
Nov 22 Javascript
JavaScript设计模式开发中组合模式的使用教程
May 18 Javascript
JS获取中文拼音首字母并通过拼音首字母快速查找页面内对应中文内容的方法【附demo源码】
Aug 19 Javascript
canvas实现环形进度条效果
Mar 23 Javascript
详解vue2.0 transition 多个元素嵌套使用过渡
Jun 19 Javascript
Auto.js自动收取自己和好友蚂蚁森林能量脚本
Jun 28 Javascript
微信小程序实现swiper切换卡内嵌滚动条不显示的方法示例
Dec 20 Javascript
利用Vue实现一个markdown编辑器实例代码
May 19 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应用程序来获取Web服务器的状态信息
2006/10/09 PHP
php error_log 函数的使用
2009/04/13 PHP
php PDO异常处理详解
2016/11/20 PHP
php获取开始与结束日期之间所有日期的方法
2016/11/29 PHP
php判断目录存在的简单方法
2019/09/26 PHP
Jquery 常用方法经典总结
2010/01/28 Javascript
JavaScript 构造函数 面相对象学习必备知识
2010/06/09 Javascript
多个js与css文件的合并方法详细说明
2012/12/26 Javascript
jQuery查询数据返回object和字符串影响原因是什么
2013/08/09 Javascript
jQuery实现Div拖动+键盘控制综合效果的方法
2015/03/10 Javascript
【经典源码收藏】jQuery实用代码片段(筛选,搜索,样式,清除默认值,多选等)
2016/06/07 Javascript
关于js二维数组和多维数组的定义声明(详解)
2016/10/02 Javascript
通过BootStrap-select插件 js jQuery控制select属性变化
2017/01/03 Javascript
从零学习node.js之详解异步控制工具async(八)
2017/02/27 Javascript
jQuery操作动画完整实例分析
2020/01/10 jQuery
vue使用video插件vue-video-player的示例
2020/10/03 Javascript
vant自定义二级菜单操作
2020/11/02 Javascript
利用python程序帮大家清理windows垃圾
2017/01/15 Python
详解python中executemany和序列的使用方法
2017/08/12 Python
Python打印“菱形”星号代码方法
2018/02/05 Python
celery4+django2定时任务的实现代码
2018/12/23 Python
解决Python plt.savefig 保存图片时一片空白的问题
2019/01/10 Python
2019 Python最新面试题及答案16道题
2019/04/11 Python
Python 实现OpenCV格式和PIL.Image格式互转
2020/01/09 Python
Tensorflow轻松实现XOR运算的方式
2020/02/03 Python
在CentOS7下安装Python3教程解析
2020/07/09 Python
python中xlutils库用法浅析
2020/12/29 Python
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
2013/07/19 HTML / CSS
审计工作个人的自我评价
2013/12/25 职场文书
渡河少年教学反思
2014/02/12 职场文书
高考寄语大全
2014/04/08 职场文书
解决hive中导入text文件遇到的坑
2021/04/07 Python
总结Python连接CS2000的详细步骤
2021/06/23 Python
RestTemplate如何通过HTTP Basic Auth认证示例说明
2022/03/17 Java/Android
Nginx+Tomcat负载均衡多实例详解
2022/04/11 Servers
详解Spring Bean的配置方式与实例化
2022/06/10 Java/Android