jquery事件绑定解绑机制源码分析

引子

为什么Jquery能实现不传回调函数也能解绑事件?如下:

$("p").on("click",function(){alert("The paragraph was clicked.");
});$("#box1").off("click");

事件绑定解绑机制

调用on函数的时候,将生成一份事件数据,结构如下:

{type: type,origType: origType,data: data,handler: handler,guid: guid,selector: selector,needsContext: needsContext,namespace: namespace
}

并将该数据加入到元素的缓存中。jquery中每个元素都可以有一个缓存(只有有需要的时候才生成),其实就是该元素的一个属性。jquery为每个元素的每种事件都建立一个队列,用来保存事件处理函数,所以可以对一个元素添加多个事件处理函数。缓存的结构如下:

"div#box":{  //元素"Jquery623873":{ //元素的缓存"events":{ "click":[{    //元素click事件的事件数据type: type,origType: origType,data: data,handler: handler,guid: guid,selector: selector,needsContext: needsContext,namespace: namespace}],"mousemove":[{type: type,origType: origType,data: data,handler: handler,guid: guid,selector: selector,needsContext: needsContext,namespace: namespace}]}}
}

当要解绑事件的时候,如果没指定fn参数,jquery就会从该元素的缓存里拿到要解绑的事件的处理函数队列,从里面拿出fn参数,然后调用removeEventListener进行解绑。

源代码

代码注释可能不太清楚,可以复制出来看

jquery原型中的on,one,off方法:

事件绑定从这里开始

jQuery.fn.extend( {on: function( types, selector, data, fn ) {return on( this, types, selector, data, fn );},one: function( types, selector, data, fn ) {return on( this, types, selector, data, fn, 1 );},off: function( types, selector, fn ) {//此处省略处理参数的代码return this.each( function() {jQuery.event.remove( this, types, fn, selector );} );}
} );

独立出来供one和on调用的on函数:

function on( elem, types, selector, data, fn, one ) {var origFn, type;//此处省略处理参数的代码//是否是通过one绑定,是的话使用一个函数代理当前事件回调函数,代理函数只执行一次//这里使用到了代理模式if ( one === 1 ) {     origFn = fn;fn = function( event ) {// Can use an empty set, since event contains the infojQuery().off( event );return origFn.apply( this, arguments );};// Use same guid so caller can remove using origFnfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );}/*************************************************** jquery将所有选择到的元素到放到一个数组里,然后*** 对每个元素到使用event对象的add方法绑定事件*************************************************/return elem.each( function() {jQuery.event.add( this, types, fn, data, selector );} );
}

处理参数的代码也可以看一下,实现on("click",function(){})这样调用 on:function(types, selector, data, fn)也不会出错。其实就是内部判断,如果data, fn参数为空的时候,把selector赋给fn

event对象是事件绑定的一个关键对象:

这里处理把事件绑定到元素和把事件信息添加到元素缓存的工作:

jQuery.event = {add: function( elem, types, handler, data, selector ) {var handleObjIn, eventHandle, tmp,events, t, handleObj,special, handlers, type, namespaces, origType,elemData = dataPriv.get( elem );   //这句将检查elem是否被缓存,如果没有将会创建一个缓存添加到elem元素上。形式诸如:elem["jQuery310057655476080253721"] = {}// Don't attach events to noData or text/comment nodes (but allow plain objects)if ( !elemData ) {return;}//用户可以传入一个自定义数据对象来代替事件回调函数,将事件回调函数放在这个数据对象的handler属性里if ( handler.handler ) {handleObjIn = handler;handler = handleObjIn.handler;selector = handleObjIn.selector;}//每个事件回调函数都会生成一个唯一的id,以后find/remove的时候会用到if ( !handler.guid ) {handler.guid = jQuery.guid++;}// 如果元素第一次绑定事件,则初始化元素的事件数据结构和主回调函数(main)//说明:每个元素有一个主回调函数,作为绑定多个事件到该元素时的回调的入口if ( !( events = elemData.events ) ) {events = elemData.events = {};}//这里就是初始化主回调函数的代码if ( !( eventHandle = elemData.handle ) ) {eventHandle = elemData.handle = function( e ) {// Discard the second event of a jQuery.event.trigger() and// when an event is called after a page has unloadedreturn typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?jQuery.event.dispatch.apply( elem, arguments ) : undefined;};}// 处理事件绑定,考虑到可能会通过空格分隔传入多个事件,这里要进行多事件处理types = ( types || "" ).match( rnotwhite ) || [ "" ];t = types.length;while ( t-- ) {tmp = rtypenamespace.exec( types[ t ] ) || [];  type = origType = tmp[ 1 ];namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();// There *must* be a type, no attaching namespace-only handlersif ( !type ) {continue;}// If event changes its type, use the special event handlers for the changed typespecial = jQuery.event.special[ type ] || {};// If selector defined, determine special event api type, otherwise given typetype = ( selector ? special.delegateType : special.bindType ) || type;// Update special based on newly reset typespecial = jQuery.event.special[ type ] || {};// 事件回调函数的数据对象handleObj = jQuery.extend( {type: type,origType: origType,data: data,handler: handler,guid: handler.guid,selector: selector,needsContext: selector && jQuery.expr.match.needsContext.test( selector ),namespace: namespaces.join( "." )}, handleObjIn );// 加入第一次绑定该类事件,会初始化一个数组作为事件回调函数队列,每个元素的每一种事件有一个队列if ( !( handlers = events[ type ] ) ) {handlers = events[ type ] = [];handlers.delegateCount = 0;// Only use addEventListener if the special events handler returns falseif ( !special.setup ||special.setup.call( elem, data, namespaces, eventHandle ) === false ) {if ( elem.addEventListener ) {elem.addEventListener( type, eventHandle );}}}if ( special.add ) {special.add.call( elem, handleObj );if ( !handleObj.handler.guid ) {handleObj.handler.guid = handler.guid;}}// 加入到事件回调函数队列if ( selector ) {handlers.splice( handlers.delegateCount++, 0, handleObj );} else {handlers.push( handleObj );}// Keep track of which events have ever been used, for event optimization// 用来追踪哪些事件从未被使用,用以优化jQuery.event.global[ type ] = true;}}
};

千万注意,对象和数组传的是引用!比如将事件数据保存到缓存的代码:

handlers = events[ type ] = [];if ( selector ) {handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {handlers.push( handleObj );
}

handlers的改变,events[ type ]会同时改变。

dataPriv就是管理缓存的对象:

其工作就是给元素创建一个属性,这个属性是一个对象,然后把与这个元素相关的信息放到这个对象里面,缓存起来。这样需要使用到这个对象的信息时,只要知道这个对象就可以拿到:

function Data() {//从这里可以看出expando 只在创建Data类的实例的时候初始化一次,即每个元素都以expando作为缓存的名称this.expando = jQuery.expando + Data.uid++;  
}Data.uid = 1;//删除部分没用到代码
Data.prototype = {cache: function( owner ) {// 取出缓存,可见缓存就是目标对象的一个属性var value = owner[ this.expando ];// 如果对象还没有缓存,则创建一个if ( !value ) {value = {};// We can accept data for non-element nodes in modern browsers,// but we should not, see #8335.// Always return an empty object.if ( acceptData( owner ) ) {// If it is a node unlikely to be stringify-ed or looped over// use plain assignmentif ( owner.nodeType ) {owner[ this.expando ] = value;// Otherwise secure it in a non-enumerable property// configurable must be true to allow the property to be// deleted when data is removed} else {Object.defineProperty( owner, this.expando, {value: value,configurable: true} );}}}return value;},get: function( owner, key ) {return key === undefined ?this.cache( owner ) :// Always use camelCase key (gh-2257) 驼峰命名owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];},remove: function( owner, key ) {var i,cache = owner[ this.expando ];if ( cache === undefined ) {return;}if ( key !== undefined ) {// Support array or space separated string of keysif ( jQuery.isArray( key ) ) {// If key is an array of keys...// We always set camelCase keys, so remove that.key = key.map( jQuery.camelCase );} else {key = jQuery.camelCase( key );// If a key with the spaces exists, use it.// Otherwise, create an array by matching non-whitespacekey = key in cache ?[ key ] :( key.match( rnotwhite ) || [] );}i = key.length;while ( i-- ) {delete cache[ key[ i ] ];}}// Remove the expando if there's no more dataif ( key === undefined || jQuery.isEmptyObject( cache ) ) {// Support: Chrome <=35 - 45// Webkit & Blink performance suffers when deleting properties// from DOM nodes, so set to undefined instead// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)if ( owner.nodeType ) {owner[ this.expando ] = undefined;} else {delete owner[ this.expando ];}}},hasData: function( owner ) {var cache = owner[ this.expando ];return cache !== undefined && !jQuery.isEmptyObject( cache );}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序导航:免费视频+精品教程+DEMO集合(长期更新)

1&#xff1a;官方工具&#xff1a;https://mp.weixin.qq.com/debug/w ... tml?t14764346784612&#xff1a;简易教程&#xff1a;https://mp.weixin.qq.com/debug/wxadoc/dev/?t14764346775993&#xff1a;设计指南&#xff1a;https://mp.weixin.qq.com/debug/wxadoc/desig…

怎么查看usb读取信息_电脑弹出USB设备时提示该设备正在使用中的解决方法

转载的文章&#xff0c;原文&#xff1a;电脑弹出USB设备时提示该设备正在使用中的解决方法&#xff0d;系统城​www.xitongcheng.com电脑弹出USB设备时提示该设备正在使用&#xff0c;虽然在正常显示时并没有程序在使用&#xff0c;但是确实会出现这种情况&#xff0c;实际上确…

js菜单自适应的实现

功能示意图 菜单自适应示意图如下&#xff1a; 对象A可以拖动&#xff0c;缩放。菜单跟随对象A拖动&#xff0c;位置优先级为下面、上面、右边、左边、中间。 实现思路 其实菜单放在上下左右中&#xff0c;都是它的left和top在变&#xff0c;我们只要依次计算菜单在下/上/右…

python转换为c代码_bash 转换为C代码

bash 转换为C代码,并编译为可执行文件[rootlocalhost ~]# wget http://www.datsi.fi.upm.es/~frosal/sources/shc-3.8.9.tgz[rootlocalhost ~]# tar xvfz shc-3.8.9.tgz[rootlocalhost ~]# cd shc-3.8.9[rootlocalhost shc-3.8.9]# gcc -c shc.c[rootlocalhost shc-3.8.9]# gcc…

promise简析

promise可以解决多个回调函数嵌套问题&#xff0c;将嵌套所表示的流程通过同步的代码更加清晰的表达出来。 promise的用法与使用配置文件很类似&#xff1a;启动异步函数&#xff0c;在then里面配置好异步函数成功或执行失败的回调函数&#xff0c;然后就可以去干别的事了。 …

python cookies过期_Python3中关于cookie的创建与保存

1.cookie的作用cookie 是指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据&#xff0c;就像有些网站上的一些数据是需要登录后才能看得到&#xff0c;那么想抓取某个页面内容&#xff0c;就需要用到cookie来模拟登陆了。在用cookie模拟登陆的时候&…

GIS-004-Cesium版权信息隐藏

.cesium-widget-credits { display: none; } .cesium-viewer .cesium-widget-credits { display: none; }转载于:https://www.cnblogs.com/defineconst/p/5965531.html

webpaper网页出题语言

目的 方便编写web试题&#xff0c;直接使用几个简单的标签&#xff0c;就可以生成一份可以在web上使用&#xff0c;并且可以打印出来的题。 效果预览 考卷打印效果 生成考卷发送给学生&#xff0c;或打印出来线下考 答案卷打印效果&#xff1a; 通过点击”显示答案”生成答…

python教程很详细_Python编程入门教程:从入门到高级,非常详细

本文的资料和内容是我下载的&#xff0c;觉得非常有用&#xff0c;于是转过来大家瞧瞧&#xff1a;这里给初学Python的朋友提供一些建议和指导吧。大神请无视&#xff0c;俗话说&#xff1a;授人以鱼不如授人以渔。所以我这里只是阐述学习过程&#xff0c;并不会直接详细写某个…

NOIP复习资料——往年习题精选

一、计算机系统 1.在以下各项中&#xff0c;&#xff08;&#xff09;不是CPU的组成部分。&#xff08;NOIP2007&#xff09; A&#xff0e;控制器B&#xff0e;运算器C&#xff0e;寄存器D&#xff0e;主板 【答案】D。CPU由控制器、运算器和寄存器组成。 2.在下列各项中…

【笔记】js重写

js重写&#xff0c;以localstorage为例&#xff1a; var originalSetItem localStorage.setItem; localStorage.setItem function(){// 这里做一些额外的事...//继续调用原来的方法originalSetItem.apply(this, arguments); } 代码引自&#xff1a;这里

(中英)作文 —— 标题与小标题

一个押韵的标题&#xff0c;会十分讨巧&#xff1b; 当世界年轻的时候&#xff0c; 当中国年轻的时候&#xff0c;当世界衰老的时候&#xff1b;1. 英文 A year of no significance. An animal of no significance.2. 名著的变形 双城记&#xff1a;A tale of Two cities Tales…

table 表格点击当前行按钮隐藏当前的另一个按钮_Excel表格为什么那么慢已经应该如何解决(四)一类特殊的函数易变函数(volatile function)...

我们&#xff0c;让Excel变简单今天我们把主题聚焦在一类特殊函数造成的问题上——volatile function。这一类函数有人把他们翻译成“易变函数”&#xff0c;其实挺贴切的。本系列其他文章&#xff1a;Excel表格为什么那么慢已经应该如何解决(一)概述Excel表格为什么那么慢已经…

h5 storage事件监听

分析 引用《h5移动web开发指南》上的话&#xff1a; “当同源页面的某个页面修改了localStorage,其余的同源页面只要注册了storage事件&#xff0c;就会触发” 所以&#xff0c;localStorage的例子运行需要如下条件&#xff1a; 同一浏览器打开了两个同源页面其中一个网页修…

python工作台_FreeCAD二次开发-创建Python工作台,添加菜单栏和工具条,FreeCAD命令

FreeCAD作为一款基于OpenCasCAD内核的开源CAD软件&#xff0c;可以在GitHub上下载源代码。阅读源代码&#xff0c;有助于我们学习CAD软件架构&#xff0c;了解底层几何算法。由博主Caesar卢尚宇自学整理(纯粹出于对三维CAD软件开发的热爱)内容出自FreeCAD官方社区https://wiki.…

3d照片环效果(修改版--添加了x轴y轴双向转动和修复模糊度的bug)

今天用用前两天总结的css3新效果写了一个3d照片环的效果&#xff0c;其中还有些bug大家可以看一看&#xff0c;一起改进。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><sty…

h5离线应用

h5离线应用 所谓离线应用&#xff0c;就是断网之后还可以运行的应用。h5提供了application cache&#xff0c;可以保存网页的离线数据。CSDN的Markdown编辑器就运用了application cache&#xff0c;在没有网络的情况下&#xff0c;一样可以正常使用&#xff0c;这就是一个很好…

[deviceone开发]-do_RichLabel的简单示例

一、简介 do_RichLabel支持html格式的文本内容&#xff0c;但是只支持部分标签&#xff0c;这个示例列出了一些支持的常用标签&#xff0c;android能支持的标签相对ios更少 二、效果图 三、相关下载 https://github.com/do-project/code4do/tree/master/richlabel_demo 四、相关…

使用rpm包升级ntpd服务_服务器准备升级,小程序将暂停使用

今天说3件事情&#xff1a;一、由于用户增速超出了我们的预期&#xff0c;为确保法助攻小程序的使用体验&#xff0c;我们购买了更高配置的服务器&#xff0c;目前&#xff0c;内部测试已完成&#xff0c;现决定对服务器进行正式升级。服务器升级时间&#xff1a;8月7日(本周五…

重装Nodejs后,webstorm代码报错问题

项描述现象之前没有问题的代码&#xff0c;突然出现很多奇怪的错误原因没有更新webstorm的Nodejs路径设置造成的解决办法重新设置webstorm的Nodejs路径&#xff0c;见下图 重新选择Nodejs安装目录&#xff0c;点击应用