读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 相关文章推荐
运用Windows XP附带的Msicuu.exe、Msizap.exe来彻底卸载顽固程序
Apr 21 Javascript
Sample script that displays all of the users in a given SQL Server DB
Jun 16 Javascript
在一个浏览器里呈现所有浏览器测试结果的前端测试工具的思路
Mar 02 Javascript
Ajax 数据请求的简单分析
Apr 05 Javascript
jQuery解决$符号命名冲突
Jun 18 Javascript
总结JavaScript的正则与其他语言的不同之处
Aug 25 Javascript
利用Vue.js+Node.js+MongoDB实现一个博客系统(附源码)
Apr 24 Javascript
js学使用setTimeout实现轮循动画
Jul 17 Javascript
vue滚动轴插件better-scroll使用详解
Oct 17 Javascript
解决layui的table插件无法多层级获取json数据的问题
Sep 19 Javascript
bootstrap实现嵌套模态框的实例代码
Jan 10 Javascript
vue表单验证之禁止input输入框输入空格
Dec 03 Vue.js
仅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
wordpress之wp-settings.php
2007/08/17 PHP
php+iframe实现隐藏无刷新上传文件
2012/02/10 PHP
PHP 数组和字符串互相转换实现方法
2013/03/26 PHP
PHP数据对象映射模式实例分析
2019/03/29 PHP
对google个性主页的拖拽效果的js的完整注释[转]
2007/04/10 Javascript
ext 代码生成器
2009/08/07 Javascript
JQuery入门—JQuery程序的代码风格详细介绍
2013/01/03 Javascript
javascript中substring()、substr()、slice()的区别
2015/08/30 Javascript
使用ionic切换页面卡顿的解决方法
2016/12/16 Javascript
angularjs中使用ng-bind-html和ng-include的实例
2017/04/28 Javascript
JS 组件系列之Bootstrap Table的冻结列功能彻底解决高度问题
2017/06/30 Javascript
JS实现访问DOM对象指定节点的方法示例
2018/04/04 Javascript
Angular5中提取公共组件之radio list的实例代码
2018/07/10 Javascript
JavaScript使用递归和循环实现阶乘的实例代码
2018/08/28 Javascript
详解实现一个通用的“划词高亮”在线笔记功能
2019/04/23 Javascript
基于js实现抽红包并分配代码实例
2019/09/19 Javascript
原生JS实现汇率转换功能代码实例
2020/05/13 Javascript
[01:01:42]Secret vs Optic Supermajor 胜者组 BO3 第二场 6.4
2018/06/05 DOTA
详解Python中的各种函数的使用
2015/05/24 Python
编写Python脚本把sqlAlchemy对象转换成dict的教程
2015/05/29 Python
Python设置在shell脚本中自动补全功能的方法
2018/06/25 Python
python使用selenium登录QQ邮箱(附带滑动解锁)
2019/01/23 Python
Python 中Django安装和使用教程详解
2019/07/03 Python
python在OpenCV里实现投影变换效果
2019/08/30 Python
numpy库reshape用法详解
2020/04/19 Python
使用python实现时间序列白噪声检验方式
2020/06/03 Python
python和node.js生成当前时间戳的示例
2020/09/29 Python
吉列剃须刀英国官网:Gillette英国
2019/03/28 全球购物
接口中的方法可以是abstract的吗
2015/07/23 面试题
图书室管理制度
2014/01/19 职场文书
高一物理教学反思
2014/01/24 职场文书
火锅店创业计划书范文
2014/02/02 职场文书
模特职业生涯规划范文
2014/02/26 职场文书
大学生找工作求职信
2014/07/09 职场文书
建筑施工安全生产责任书
2014/07/22 职场文书
心得体会该怎么写呢?
2019/06/27 职场文书