读jQuery之十一(添加事件核心方法)
1 2 3 4 5 6 7 | 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了。
下面详细说明
1 2 3 | if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return ; } |
文本和注释节点直接返回。
1 2 3 4 5 6 | if ( handler === false ) { handler = returnFalse; } else if ( !handler ) { // Fixes bug #7229. Fix recommended by jdalton return ; } |
参数handler为false时,将handler赋值为returnFalse,returnFalse为一个函数,如下
1 2 3 | function returnFalse() { return false ; } |
jQuery通过handler为false来阻止元素默认行为,停止事件冒泡。这个需要结合jQuery.event.handle看。
1 2 3 4 5 6 7 8 9 10 11 | 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类型。如下
1 2 3 4 5 | 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。这话有点绕,慢慢体会。
1 2 3 4 | // Make sure that the function being executed has a unique ID if ( !handler.guid ) { handler.guid = jQuery.guid++; } |
所传参数handler添加个属性guid,为一个数字,自增的从1开始。即使用jQuery添加事件,会为事件响应函数默认的添加了属性guid。这个guid再删除事件时会用到。
1 2 | // Init the element's event structure var elemData = jQuery._data( elem ); |
先取elemData,这里使用了前面提到的jQuery._data。第一次为HTMLElement添加事件是elemData是个空对象({})。
1 2 3 4 5 | // If no elemData is found then we must be trying to bind to one of the // banned noData elements if ( !elemData ) { return ; } |
elemData不存在则直接返回。
1 2 | var events = elemData.events, eventHandle = elemData.handle; |
定义events,eventHandle。同样第一次时这两个变量都是undefined。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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赋值。
1 2 3 | // 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,避免部分浏览器中内存泄露。
1 2 3 | // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = types.split( " " ); |
将字符串以空格为切割符转成数组。这句使其可以一次添加多个事件,多个事件的handler是相同的。
后面是一个while循环
1 2 3 4 5 6 7 8 | 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中内存泄露。
1 2 | // Nullify elem to prevent memory leaks in IE elem = null ; |
jQuery事件管理的数据结构,我做了个图。如下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端