jQuery-1.9.1源码分析系列(十) 事件系统——事件绑定

  事件绑定的方式有很多种。使用了jQuery那么原来那种绑定方式(elem.click = function(){...})就不推荐了,原因?

  最主要的一个原因是elem.click = fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果。

  

  看一下jQuery绑定事件的方式有哪些

jQuery.fn.eventType([[data,] fn])

  比如eventType指的是事件类型,比如click: $("#chua").click(fn);

  data这个参数一般都不会使用。这种方式事件绑定在("#chua")上,没有委托事件,和js原生的事件绑定更接近。我们看一下源码

jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {//合并15种事件统一增加到jQuery.fn上,内部调用this.on / this.triggerjQuery.fn[ name ] = function( data, fn ) {return arguments.length > 0 ?this.on( name, null, data, fn ) ://如果不带参数表示立刻触发指定事件this.trigger( name );};
});

 

jQuery.fn.bind( types[, data], fn )

  比如$("#chua").bind("click",fn)。直接将事件绑定到$("#chua")上,没有委托事件。源码

bind: function( types, data, fn ) {return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {return this.off( types, null, fn );
}

 

jQuery.fn.delegate(selector, types[, data], fn)

  顾名思义delegate这个函数是用来做事件委托的,将选择器selector对应的响应处理委托给当前jQuery所匹配的元素。

  比如:$(document).delegate('#big',"click",dohander);分析到这里顺便分析一下事件委托的处理流程

  当点击"#big"元素的时候,事件click会冒泡直到document节点;

  document绑定了处理事件,这个处理事件会调用到事件分发器dispatch;

  dispatch中取出对应事件类型click的所有的委托事件列表handlers;

  根据事件源event.target过滤出委托事件列表handlers中每一个元素的selector属性对应的节点处于事件原和委托节点document之间(包括事件源)的委托事件,保存为handlerQueue;

  执行handlerQueue里面的事件处理。

  上面是一个大致的流程,后续会详细分析。先看delegate源码

delegate: function( selector, types, data, fn ) {return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {// ( namespace ) or ( selector, types [, fn] )return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
}

 

jQuery.fn.one( types[, selector[, data]], fn )

  通过one()函数绑定的事件处理函数都是一次性的,只有首次触发事件时会执行该事件处理函数。触发之后,jQuery就会移除当前事件绑定。

  比如$("#chua").one("click",fn);为#chua节点绑定一次性的click事件

  $(document).one("click","#chua",fn);将#chua的click事件委托给document处理。源码

one: function( types, selector, data, fn ) {return this.on( types, selector, data, fn, 1 );
}

 

jQuery.fn.trigger(type[, data])

jQuery.fn.triggerHandler(type[, data])

  trigger触发jQuery对象所匹配的每一个元素对应type类型的事件。比如$("#chua").trigger("click");

  triggeHandler只触发jQuery对象所匹配的元素中的第一个元素对应的type类型的事件,且不会触发事件的默认行为。

//立刻触发jQuery对象内所有元素的指定type的事件
trigger: function( type, data ) {return this.each(function() {jQuery.event.trigger( type, data, this );});
},
//立刻触发jQuery对象内第一个元素的指定type的事件,且不会触发事件(比如表单提交)的默认行为
triggerHandler: function( type, data ) {var elem = this[0];if ( elem ) {return jQuery.event.trigger( type, data, elem, true );}
}

  

  上面分析了那么些个事件绑定,有么有发现他们都是使用.on方式绑定的?这也是为什么提倡统一使用on来绑定的原因(one方式除外)。

jQuery.fn.on( types[, selector[, data]], fn )

  .on的事件绑定一半的代码都实在处理传递不同参数的处理,这也是jQuery的口号Write less, do more的代价吧。最终使用jQuery.event.add来绑定事件。

  jQuery.event.add绑定事件有几个比较关键的地方:

  第一个,使用内部缓存来保存节点elem的事件信息

            //获取缓存数据 
       elemData = jQuery._data( elem );...
       
       //设置缓存数据
if ( !(events = elemData.events) ) {events = elemData.events = {};}if ( !(eventHandle = elemData.handle) ) {eventHandle = elemData.handle = function( e ) {...};//将elem作为handle函数的一个特征防止ie非本地事件引起的内存泄露eventHandle.elem = elem;}

  第二个,设置绑定事件信息,特别是指定的选择器selector、响应处理handler、响应事件类型type、命名空间namespace

        // handleObj:设置绑定事件信息。贯穿整个事件处理handleObj = jQuery.extend({type: type,origType: origType,data: data,handler: handler,guid: handler.guid,selector: selector,// For use in libraries implementing .is(). We use this for POS matching in `select`//"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +//whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )//用来判断亲密关系needsContext: selector && jQuery.expr.match.needsContext.test( selector ),namespace: namespaces.join(".")}, handleObjIn );

  第三个,节点的事件列表中,真正的委托事件列表放置在前面,和delegateCount属性同步,即events.click.length假设为3,events.click.delegateCount假设为2。那么events.click[0]和events.click[1]所指定事件是委托事件。第三个events.click[2]对应的事件不是委托事件,而是节点自身的事件。

        //将事件对象handleObj添加到元素的处理列表,委托事件放在前面,委托代理计数递增if ( selector ) {handlers.splice( handlers.delegateCount++, 0, handleObj );} else {handlers.push( handleObj );}

  源码和添加事件后的结构上一章已经分析,详情请点击查看

  

  绑定有一个公用函数jQuery.fn.on。解绑同样有一个公用函数jQuery.fn.off

jQuery.fn.off([ types[, selector][, fn]] )

  这里的传参有个比较特殊的情况:当types是浏览器事件对象event的时候,表示要去掉(解绑)委托节点上event.selector指定的委托事件

//传入的参数是事件且绑定了处理函数
if ( types && types.preventDefault && types.handleObj ) {// ( event )  dispatched jQuery.EventhandleObj = types.handleObj;//types.delegateTarget是事件托管对象
        jQuery( types.delegateTarget ).off(//组合jQuery识别的typehandleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,handleObj.selector,handleObj.handler);return this;
}

  无论如何最终都是调用jQuery.event.remove函数来解绑事件。

  jQuery.fn.off完整的源码如下

off: function( types, selector, fn ) {var handleObj, type;//传入的参数是事件且绑定了处理函数if ( types && types.preventDefault && types.handleObj ) {// ( event )  dispatched jQuery.EventhandleObj = types.handleObj;//types.delegateTarget是事件托管对象
        jQuery( types.delegateTarget ).off(//组合jQuery识别的typehandleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,handleObj.selector,handleObj.handler);return this;}if ( typeof types === "object" ) {// ( types-object [, selector] )for ( type in types ) {this.off( type, selector, types[ type ] );}return this;}if ( selector === false || typeof selector === "function" ) {// ( types [, fn] )fn = selector;selector = undefined;}if ( fn === false ) {fn = returnFalse;}return this.each(function() {jQuery.event.remove( this, types, fn, selector );});
}
View Code

  

  接下来分析一下事件解绑的低级api jQuery.event.remove。

jQuery.event.remove

  jQuery使用.off()函数伤处绑定的事件时内部调用的基础函数是jQuery.event.remove。该函数的处理流程如下

  1. 分解传入的要删除的事件类型types,遍历类型,如果要删除的事件没有事件名,只有命名空间则表示删除该命名空间下所有绑定事件

//分解types为type.namespace为单位元素的数组
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {tmp = rtypenamespace.exec( types[t] ) || [];type = origType = tmp[1];namespaces = ( tmp[2] || "" ).split( "." ).sort(); //解绑当前元素的当前命名空间(types[ t ])上所有的事件if ( !type ) {for ( type in events ) {jQuery.event.remove( elem, type + types[ t ], handler, selector, true );}continue;}...

  2. 遍历类型过程中,删除匹配的事件,代理计数修正

type = ( selector ? special.delegateType : special.bindType ) || type;
handlers = events[ type ] || [];
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );//删除匹配事件
origCount = j = handlers.length;
while ( j-- ) {handleObj = handlers[ j ]; //各种满足移除事件的条件才能移除if ( ( mappedTypes || origType === handleObj.origType ) &&( !handler || handler.guid === handleObj.guid ) &&( !tmp || tmp.test( handleObj.namespace ) ) &&( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {handlers.splice( j, 1 ); 
        if ( handleObj.selector ) {handlers.delegateCount--;}
        if ( special.remove ) {special.remove.call( elem, handleObj );}}
}

  3. 如果节点上指定类型的事件处理器已经为空,则将events上的该类型的事件处理对象移除

// 移除事件处理对象
// (移除特殊事件处理过程中避免潜在的无限递归,下一章会专门详解这种情况)
if ( origCount && !handlers.length ) {
//例如 var js_obj = document.createElement("div"); js_obj.onclick = function(){ …}
//上面的js_obj是一个DOM元素的引用,DOM元素它长期在网页当中,不会消失,而这个DOM元素的一属性onclick,又是内部的函数引用(闭包),而这个匿名函数又和js_obj之间有隐藏的关联(作用域链)所以形成了一个,循环引用.
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {jQuery.removeEvent( elem, type, elemData.handle );
} delete events[ type ];
}

  4. 如果节点上没有任何绑定的事件,则清空事件处理入口handle

if ( jQuery.isEmptyObject( events ) ) {
  delete elemData.handle;
  //removeData还检事件对象是否为空,所以使用它替代deletejQuery._removeData( elem, "events" );
}

 

拓展: 浏览器事件删除jQuery.removeEvent

jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {if ( elem.removeEventListener ) {elem.removeEventListener( type, handle, false );}
} :
function( elem, type, handle ) {var name = "on" + type;if ( elem.detachEvent ) {// #8545, #7054,避免自定义事件在IE6-8中的内存泄露// detachEvent需要传递第一个参数,不能是undefined的if ( typeof elem[ name ] === core_strundefined ) {elem[ name ] = null;}elem.detachEvent( name, handle );}
};

 

   如果觉得本文不错,请点击右下方【推荐】!

 

 

 

  

转载于:https://www.cnblogs.com/chuaWeb/p/jQuery-1-9-1-addEvents-2.html

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

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

相关文章

Windows 8系统平台上应用软件安装心得

1.ArcGIS 10.2安装 需要单独安装.NET 3.5,GIS软件自带的.NET系统不识别,点击360云盘地址进行下载(提取码为:1ed3)。(另外,Win8系统上安装.NET可以参考:http://blog.csdn.net/aijavaer/article/d…

JavaFX 一 出生新手村(阅读小规则)

我就不讲IDE怎么装的,网上有的是,我仅仅是说说我学习过程中遇到的,该注意的东西 1.JavaFX刚開始出是基于脚本script开发的语言,所以网上会有流传比較多关于script的JavaFX,对于被甲骨文大大(Oracle)改版后的JavaFX已经不太有用了,如今都基本靠java代码编程了,所以网上找到的scr…

Android之jni编译出现multiple definition of ‘××××ב

1、问题 编译ndk代码的时候提示 multiple definition of 2、原因 多个文件包含同一个头文件时,在Android.mk文件里面写了2个文件 _src_files : \common/A.cpp\common/A.cpp\ 3、解决办法 去掉一个文件,然后再次编译。

Android视图绘制流程完全解析,带你一步步深入了解View(二)

转自:http://blog.csdn.net/guolin_blog/article/details/16330267 在上一篇文章中,我带着大家一起剖析了一下LayoutInflater的工作原理,可以算是对View进行深入了解的第一步吧。那么本篇文章中,我们将继续对View进行深入探究&…

C# 线程问题之死锁

过多的锁定也会有麻烦。在死锁中,至少有两个线程被挂起,并等待对方解除锁定。由于两个线程都在等待对方,就出现了死锁,线程将无限等待下去。为了说明死锁,下面实例化 StateObject 类型的两个对象,并把它们传…

java requestbody map_@RequestBody 的正确使用办法

1.以前一直以为在SpringMVC环境中,RequestBody接收的是一个Json对象,一直在调试代码都没有成功,后来发现,其实 RequestBody接收的是一个Json对象的字符串,而不是一个Json对象。然而在ajax请求往往传的都是Json对象&…

Matlab图形绘制

1.正余弦曲线 例如自变量从0到10,间隔为0.1的曲线代码如下: 正弦 t 0:.1:10; y sin(t); plot(t,y); 余弦 t 0:.1:10; y cos(t); plot(t,y); 正余弦图形显示如下:

java之异常java.net.MalformedURLException解决办法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程 1、问题 日志打印 System.err W java.net.MalformedURLExceptionW at java.net.URL.<init>(URL.java:152)W at java.…

java遇见的问题分析

下面就一些java的一些基本问题进行解释。其中蓝色部分为handsomecui的主观看法 一.synchronized(obj)里面的参数怎么解释&#xff1f; synchronized的参数代表的是“对象锁”代表的是不同的线程在synchronized块里&#xff0c;同时只有一个线程能执行该代码块,而类的不同实例之…

linq中的castT()及OfTypeT()

DataTable dt...........//获取从数据库中取出的数据(假设只有一条记录) //Cast<T>()用来将非泛型的序列转换为泛型的序列 DataRow rowdt.Rows.Cast<DataRow>().Single(); //OfType<T>():用来将序列中可以转换的转换为指定的序列 如&#xff1a;一个object数…

Jar包转成Dll的方式(带嵌套的jar也能做) (转)

研究很好几天&#xff0c;终于成功了。因为写了一个Java的项目&#xff0c;现在要求要改写成C#版本的。但是其中用到了svnkit&#xff0c;svnkit是java平台的。改写成C#的话&#xff0c;要使用SharpSVN,但是SharpSVN的API文档真的很不专业。看来看去还是svnkit比较好。所以打算…

Blazor University (5)组件 — 字面量、表达式和指令

原文链接&#xff1a;https://blazor-university.com/components/literals-expressions-and-directives/字面量、表达式和指令源代码[1]请注意&#xff0c;本节一般不涵盖 Razor 标记。它不会涵盖诸如条件输出、循环等内容。该主题在网络和书籍中的其他地方得到了广泛的介绍。使…

linux之/usr/local/bin和/usr/bin区别

1、问题 我们一般习惯把**.py文件放到/usr/local/bin这个目录下面去&#xff0c;然后可以在linux系统里面任意一个目录执行这个python文件&#xff0c;然后和/usr/bin/这个目录一般有啥区别&#xff1f; 2、区别 usr 指 Unix System Resource&#xff0c;/usr 目录包含所有的命…

java metrics 简书_Spring Boot Metrics

Spring Metricshttps://docs.spring.io/spring-metrics/docs/current/public/prometheusSpring Boot Metrics监控之Prometheus&Grafana - 简书https://www.jianshu.com/p/afc3759e75b9Metrics教程 - 简书https://www.jianshu.com/p/effe8e259d25Java监控类库Metrics - Fang…

磨刀不误砍柴工

1.emulator n. 仿真器&#xff0c;模拟器&#xff1b;竞争者 网络释义 专业释义 英英释义仿真设备 竞争者 硬件仿真器 模仿器 短语Emulator ROMs 模拟器游戏大全Network Emulator 网络仿真器 ; 网络模拟器emulator command 仿真器命令 2.variogramn.变差函数&#xff0c;变…

.NET6之MiniAPI(二十七):Metrics

应用的各种Metrics是保证应用健康稳定运行的基础&#xff0c;特别对于一些可用性有所要求的应用&#xff0c;本文介绍prometheus-net这个三方指示库。prometheus-net的工作原理是&#xff0c;在应用内部埋点&#xff0c;通过prometheus采集数据&#xff0c;然后通过grafana把采…

面向对象的优点

符合人类的思维习惯,使客户和软件设计人员之间,软件开发人员内部交流更加流畅,同时有代码重用性高,可靠性高等优点,大大提高了软件的设计和开发效率转载于:https://www.cnblogs.com/Allen974103107/p/4979596.html

解决 web.xml is missing and failOnMissingWebXml is set to true 报错

在学习maven模块化构建项目的时候遇到了如下报错信息&#xff1a; web.xml is missing and <failOnMissingWebXml> is set to true。 这时候需要右击项目——>Java EE Tools——>Generate Deployment Descriptor Stub.然后系统会在src/main/webapp/WEB_INF文件加下…

Swift游戏实战-跑酷熊猫 01 创建工程导入素材

原文:Swift游戏实战-跑酷熊猫 01 创建工程导入素材在这节里&#xff0c;我们将建立一个游戏工程&#xff0c;并导入一些必要的素材&#xff0c;例如序列帧动画文件&#xff0c;声音素材文件。动画文件我们使用atlas形式。在打包发布或者模拟器测试的时候&#xff0c;它会将整个…

【ArcGIS风暴】ArcGIS获取一个省各个地区界内的河流的总长度--以甘肃省为例

【问题分析】实际工作中,我们经常需要进行统计分析,如统计甘肃省各个地区界内的河流、公路、铁路等的总长度,统计各个地区界内总人口、作物产量、村镇分布等等。ArcGIS提供了强大的空间统计功能,可以一次性操作完成所有操作,省时省力,大大提高了工作效率,减少了错误的发…