读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实现 获取鼠标点击位置坐标的方法
Apr 12 Javascript
setTimeout和setInterval的深入理解
Nov 08 Javascript
js中的时间转换—毫秒转换成日期时间的示例代码
Jan 26 Javascript
获取select元素被选中的文本内容的js代码
Jan 29 Javascript
jquery中常用的函数和属性详细解析
Mar 07 Javascript
JS作为值的函数用法示例
Jun 20 Javascript
jQuery实现的无限级下拉菜单功能示例
Sep 12 Javascript
使用jquery给指定的table动态添加一行、删除一行
Oct 13 Javascript
JS字符串统计操作示例【遍历,截取,输出,计算】
Mar 27 Javascript
webuploader实现上传图片到服务器功能
Aug 16 Javascript
浅谈webpack4.x 入门(一篇足矣)
Sep 05 Javascript
JavaScript怎样在删除前添加确认弹出框?
May 27 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 5.0对象模型深度探索之绑定
2006/09/05 PHP
php 无法加载mcrypt.dll的解决办法
2013/04/03 PHP
PHP转盘抽奖接口实例
2015/02/09 PHP
Thinkphp事务操作实例(推荐)
2017/04/01 PHP
基于jquery的loading 加载提示效果实现代码
2011/09/01 Javascript
jQuery代码优化之基本事件
2011/11/01 Javascript
jquery使整个div区域可以点击的方法
2015/06/24 Javascript
不想让浏览器运行javascript脚本的方法
2015/11/20 Javascript
JS简单实现无缝滚动效果实例
2016/08/24 Javascript
浅谈JS如何实现真正的对象常量
2017/06/25 Javascript
详解前端路由实现与react-router使用姿势
2017/08/07 Javascript
react-native使用leanclound消息推送的方法
2018/08/06 Javascript
JavaScript函数式编程(Functional Programming)箭头函数(Arrow functions)用法分析
2019/05/22 Javascript
[50:28]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 Newbee vs KG
2018/04/01 DOTA
[25:59]Newbee vs TNC 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
[04:20]DOTA2-DPC中国联赛 正赛 VG vs LBZS 选手采访 1月19日
2021/03/11 DOTA
python flask中静态文件的管理方法
2018/03/20 Python
python opencv实现图片旋转矩形分割
2018/07/26 Python
padas 生成excel 增加sheet表的实例
2018/12/11 Python
Python反爬虫技术之防止IP地址被封杀的讲解
2019/01/09 Python
详解如何在cmd命令窗口中搭建简单的python开发环境
2019/08/29 Python
wxpython+pymysql实现用户登陆功能
2019/11/19 Python
pycharm中import呈现灰色原因的解决方法
2020/03/04 Python
python实现将列表中各个值快速赋值给多个变量
2020/04/02 Python
详解Python多线程下的list
2020/07/03 Python
一款基于css3麻将筛子3D翻转特效的实例教程
2014/12/31 HTML / CSS
canvas学习笔记之绘制简单路径
2019/01/28 HTML / CSS
Lands’ End官网:经典的美国生活方式品牌
2016/08/14 全球购物
法国时尚品牌乐都特瑞士站:La Redoute瑞士
2016/09/05 全球购物
C++:memset ,memcpy和strcpy的根本区别
2013/04/27 面试题
师范生自荐信
2013/10/27 职场文书
群众路线查摆问题整改措施
2014/10/10 职场文书
2014年党小组工作总结
2014/12/20 职场文书
交心谈心活动总结
2015/05/11 职场文书
联欢会开场白
2015/06/01 职场文书
Java 数据结构七大排序使用分析
2022/04/02 Java/Android