读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 相关文章推荐
js 获取浏览器高度和宽度值(多浏览器)
Sep 02 Javascript
Javascript 日期处理之时区问题
Oct 08 Javascript
Eclipse下jQuery文件报错出现错误提示红叉
Jan 13 Javascript
浅析Node.js中使用依赖注入的相关问题及解决方法
Jun 24 Javascript
基于javascript实现图片预加载
Jan 05 Javascript
vue表单绑定实现多选框和下拉列表的实例
Aug 12 Javascript
Vue 组件间的样式冲突污染
Aug 31 Javascript
angular之ng-template模板加载
Nov 09 Javascript
Vue组件库发布到npm详解
Feb 17 Javascript
vue双向数据绑定知识点总结
Apr 18 Javascript
基于Vue+elementUI实现动态表单的校验功能(根据条件动态切换校验格式)
Apr 04 Javascript
浅谈vue异步数据影响页面渲染
Oct 29 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
德生1994机评
2021/03/02 无线电
在php中取得image按钮传递的name值
2006/10/09 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
2017/08/11 PHP
Laravel框架Eloquent ORM简介、模型建立及查询数据操作详解
2019/12/04 PHP
JS中剪贴板兼容性、判断复制成功或失败
2021/03/09 Javascript
jquery last-child 列表最后一项的样式
2010/01/22 Javascript
jquery 仿QQ校友的DIV模拟窗口效果源码
2010/03/24 Javascript
基于jquery的横向滚动条(滑动条)
2011/02/24 Javascript
jQuery新闻滚动插件 jquery.roller.js
2011/06/27 Javascript
Eclipse引入jquery报错如何解决
2015/12/01 Javascript
JS实现微信里判断页面是否被分享成功的方法
2017/06/06 Javascript
VUE实现表单元素双向绑定(总结)
2017/08/08 Javascript
使用D3.js制作图表详解
2017/08/13 Javascript
vue引入ueditor及node后台配置详解
2018/01/03 Javascript
解决webpack无法通过IP地址访问localhost的问题
2018/02/22 Javascript
vue2.0+ 从插件开发到npm发布的示例代码
2018/04/28 Javascript
uploadify插件实现多个图片上传并预览
2019/09/30 Javascript
Laravel 如何在blade文件中使用Vue组件的示例代码
2020/06/28 Javascript
盘点提高 Python 代码效率的方法
2014/07/03 Python
Python 爬虫多线程详解及实例代码
2016/10/08 Python
python中如何使用正则表达式的非贪婪模式示例
2017/10/09 Python
Python使用try except处理程序异常的三种常用方法分析
2018/09/05 Python
linux安装Python3.4.2的操作方法
2018/09/28 Python
python3 实现一行输入,空格隔开的示例
2018/11/14 Python
Python在cmd上打印彩色文字实现过程详解
2019/08/07 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
python中读入二维csv格式的表格方法详解(以元组/列表形式表示)
2020/04/24 Python
python interpolate插值实例
2020/07/06 Python
电影T恤、80年代T恤和80年代服装:TV Store Online
2020/01/05 全球购物
应届大学生自荐信
2013/12/05 职场文书
优秀学生干部个人事迹材料
2014/06/02 职场文书
2014年检验科工作总结
2014/11/22 职场文书
建国70周年的心得体会(2篇)
2019/09/20 职场文书
redis实现排行榜功能
2021/05/24 Redis
科普 | 业余无线电知识-波段篇
2022/02/18 无线电