读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鼠标滑过弹出层的定位IE6bug解决办法
Dec 26 Javascript
通过JavaScript使Div居中并随网页大小改变而改变
Jun 24 Javascript
JS调用CS里的带参方法实例
Aug 01 Javascript
用js的for循环获取radio选中的值
Oct 21 Javascript
有效提高JavaScript执行效率的几点知识
Jan 31 Javascript
jQuery实现Tab菜单滚动切换的方法
Sep 21 Javascript
提高JavaScript执行效率的23个实用技巧
Mar 01 Javascript
EasyUI Tree树组件无限循环的解决方法
Sep 27 Javascript
基于Vuejs的搜索匹配功能实现方法
Mar 03 Javascript
django简单的前后端分离的数据传输实例 axios
May 18 Javascript
JS加载解析Markdown文档过程详解
May 19 Javascript
解决vue动态下拉菜单 有数据未反应的问题
Aug 06 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编写的简单页面跳转功能实现代码
2013/11/27 PHP
php判断文件夹是否存在不存在则创建
2015/04/09 PHP
php基于session实现数据库交互的类实例
2015/08/03 PHP
php解决和避免form表单重复提交的几种方法
2016/08/31 PHP
Aster vs Newbee BO5 第二场2.19
2021/03/10 DOTA
用正则xmlHttp实现的偷(转)
2007/01/22 Javascript
jQuery Tools tooltip使用说明
2012/07/14 Javascript
Jquery ajax执行顺序 返回自定义错误信息(实例讲解)
2013/11/06 Javascript
JS小功能(checkbox实现全选和全取消)实例代码
2013/11/28 Javascript
不同js异步函数同步的实现方法
2016/05/28 Javascript
JS去除重复并统计数量的实现方法
2016/12/15 Javascript
ThinkPHP+jquery实现“加载更多”功能代码
2017/03/11 Javascript
layui表格checkbox选择全选样式及功能的实例
2018/03/07 Javascript
webpack4.x打包过程详解
2018/07/18 Javascript
详解多页应用 Webpack4 配置优化与踩坑记录
2018/10/16 Javascript
antd组件Upload实现自己上传的实现示例
2018/12/18 Javascript
[02:03]永远的信仰DOTA2 中国军团历届国际邀请赛回顾
2016/06/26 DOTA
python 获取et和excel的版本号
2009/04/09 Python
Python实现获取网站PR及百度权重
2015/01/21 Python
Python获取运行目录与当前脚本目录的方法
2015/06/01 Python
快速排序的四种python实现(推荐)
2019/04/03 Python
Python实现的ftp服务器功能详解【附源码下载】
2019/06/26 Python
Python Django中间件使用原理及流程分析
2020/06/13 Python
Python下载的11种姿势(小结)
2020/11/18 Python
适合各种场合的美食礼品:Harry & David
2016/08/03 全球购物
下列程序在32位linux或unix中的结果是什么
2015/01/26 面试题
令人啧啧称赞的经理推荐信
2013/11/07 职场文书
职业技术学校毕业生推荐信
2013/12/03 职场文书
工作自荐信
2013/12/11 职场文书
优秀的茶餐厅创业计划书
2014/01/03 职场文书
小学毕业寄语大全
2014/04/03 职场文书
学习优秀共产党员先进事迹思想报告
2014/09/17 职场文书
意外伤害赔偿协议书范文
2014/09/23 职场文书
2014年纪检监察工作总结
2014/11/11 职场文书
葬礼主持词
2015/07/02 职场文书
如何使用Python提取Chrome浏览器保存的密码
2021/06/09 Python