手机版

jQuery 1.9.1源代码分析系列(X)事件系统的主动触发事件和模拟气泡处理

时间:2021-09-21 来源:互联网 编辑:宝哥软件园 浏览:

发现了一个以前没人注意到的小点。

stopPropagation:函数(){ var e=this.originalEvent.if(e . stopperpagation){ e . stopperpagation();}jQuery重载了stopperpagation函数调用的本地事件对象的stopperpagation函数,以防止冒泡。也就是说,防止冒泡的是当前节点,而不是事件源。

说到触发事件,我们的第一反应是使用$(.).单击()触发单击事件。这种方法无疑是简单明了的,如果能用的话建议使用。但如果是定制活动呢?例如,定义一个$(文档)。on ('Chua click ',' # middle ',fn);这种情况如何引发事件?这将使用$ ('# middle ')。触发器('蔡氏点击')。

A.触发器事件低级API——jQuery.event.trigger

触发功能支持触发所有类型的事件。这些事件主要分为两类:普通浏览器事件(包括带有名称空间的事件,如' click.chua ')和自定义事件。因为统一处理,函数的内部实现不调用。click()为普通浏览器事件做快捷处理,但统一了流程。处理过程如下。

1.获取要触发的事件(传入的事件可能是事件类型,而不是事件对象)。

event=event[ jQuery.expando ]?事件:新的jQuery。事件(类型,事件类型=='object '事件);2.纠正浏览器事件(主要是纠正事件源)和组合正确的事件处理参数数据。

If (type.indexOf(' . ')=0) {//命名空间为的事件触发器;事件类型类型命名空间=type.split(' . ')所使用的事件处理入口函数句柄()首先被取出;type=namespace . shift();namespace . sort();//调用方可以传递jQuery。事件对象,普通对象,甚至字符串事件=event[ jQuery.expando]?事件:新jQuery。事件(类型,事件类型=='object '事件);event.isTrigger=trueevent . namespace=namespace . join(' . ');event . namespace _ re=event . namespace?新正则表达式('(^|\\.)”命名空间. join('\\。(?*\\.|)') '(\\.| $)'): null;//重置result属性以避免最后一个结果剩余事件,result=undefined;if(!event . target){ event . target=elem;}//克隆参数数据,将事件放在参数数据之前,创建事件处理入口函数的参数列表。创建后,结果可能是[事件,数据] data=data==null?[事件] : jQuery.makeArray(数据,[事件]);稍后的组合事件处理参数列表数据在稍后的处理过程中被调用。

if (handle ) { handle.apply(cur,data);}3.判断是否是特殊节点对象的特殊事件,如果是,则进行特殊处理。

special=jquery . event . special[type]| | { };if(!only handlers special . trigger special . trigger . apply(elem,data)==false){ return;}需要特殊处理的事件很少,在此列出。

Special: {单击。trigger : function(){//checkbox,触发本地事件以确保正确的状态if (jquery。nodename (this,' input ')this . type==' checkbox ' this . click){ this . click();返回false}},专注。trigger : function(){//触发本地事件以确保正确的失焦/聚焦序列if (this!==document . activeelement this . focus){ try { this . focus();返回false} catch (e ) { //Support: IE9 //如果我们将焦点错误转移到隐藏元素(#1486,#12518),//let。trigger()运行处理程序} } },blur.trigger:函数(){ if(this===document . activeelement this . blur){ this . blur();返回false} }}4.从事件源遍历父节点到窗口对象,并保存传递的节点(将其保存到事件路径)以备后用。

for(;curcur=cur . parent node){ event path . push(cur);tmp=cur}//按window进入eventPath(例如,既不是普通对象也不是断开的DOM)if(tmp====(elem。owner document | | document)){ event path . push(tmp。defaultview | | tmp。parent window | | window);}5.循环之前保存的节点,访问节点缓存,如果有对应的事件类型处理队列,取出其绑定的事件(entry函数)并调用。

//jQuery绑定函数处理:判断对应的事件处理函数是否保存在节点缓存中,如果是,执行handle=(jquery。_ data (cur,' events') | | {}) [event。类型] jquery。_ data (cur,‘handle’);if (handle ) { handle.apply(cur,data);}//本地绑定处理句柄=on type cur[on type];if(handle jquery . accept data(cur)handle . apply handle . apply(cur,data)==false){ event . preventdefault();}6.最后,处理浏览器默认事件,例如提交标记的提交表单处理。

//如果没有人阻止默认处理,执行if(!onlyHandlers!事件。是默认阻止的()){ 0.}注意:普通事件加上命名空间仍然是普通事件,普通调用方法仍然发挥作用。例如,$(文档)。打开('点击。Chua ',' # id ',fn1)。on ('click ',' # id ',fn2);当您单击“#id”节点时,fn1仍将被调用。触发命名空间事件的唯一方法是触发器:$ ('# id ')。触发器('点击。Chua '),在这种情况下,将只调用fn1。

从第4步和第5步,我们可以看到trigger的另一个很棒的功能,——,模拟冒泡处理。后面会分析。

B.事件特殊处理的详细说明jQuery.event.special(主要包括事件替换和气泡模拟)。

委托设计是基于事件的,可以冒泡。但是,有些事件不能冒泡,有些事件在不同的浏览器中支持不同的冒泡条件。也有不同的浏览器支持不同类型的事件。这些过程主要放在jQuery.event.special中jQuery.event.special对象保存了适应特定事件所需的变量和方法。

有:

delegateType/BindType(用于事件类型调整)设置(第一次绑定事件时调用)add(绑定事件时调用)remove(未绑定事件时调用)distruct down(所有事件绑定未绑定时调用)trigger(发生内部触发器事件时调用)noBubble_defaulthandle。(在事件实际触发时调用)preDispatch(在事件实际触发前调用)postDispatch(在事件实际触发后调用)。

看看模拟冒泡的函数模拟。

模拟:函数(type,elem,event,bubble){//构建一个新的事件来区分之前绑定的事件。//新构建的事件避免了防止冒泡,但是如果模拟的事件可以防止默认操作,我们也会这样做来防止默认操作。var e=jQuery.extend(新jQuery。Event(),event,{ type: type,isSimulated: true,originalevent : } });if(bubble){ jquery . event . trigger(e,null,elem);} else { jquery . event . dispatch . call(elem,e);} if(e . isdefaultprevented()){ event . preventdefault();}}看到了吧,真正的气泡模拟函数是jQuery.event.trigger函数。

特别1组

这就涉及到气泡处理的问题。

special: { load: { //阻止触发图像。加载事件冒泡到窗户。加载no ubble : true },点击: {//复选框触发时保证状态正确触发器:函数(){如果(.){这个。单击();返回false}} },focus: { //触发本当前节点模糊/聚焦事件确保队列正确触发器:函数(){如果(这个!==文档。activeelement这个。焦点){试试这个。焦点();返回false} catch (e ) { //IE9,如果我们错误的让隐藏的节点获取焦点(#1486, #12518), //让。触发器()运行处理器} } },delegateType: 'focusin' },blur : { trigger : function(){ if(this===document。activeelement这个。{这个。blur();返回false} },delegateType: 'focusout' },在卸载: { post dispatch :之前函数(事件){ //即使的返回值等于未定义,火狐仍然会显示警告if (event.result!==未定义){事件。原创。返回值=事件。结果;} } } }聚焦/模糊本来是不冒泡的,但是我们依然可以通过$(文档)。on('focus ',' #left ',fn)来绑定,是怎么做到的?我们来看jQuery的处理

第一步,将集中绑定的事件转化为focusin来绑定,focusin在万维网路联盟(Consortium环球网简称W3C)的标准中是冒泡的,除开火狐之外的浏览器也确实支持冒泡(火狐浏览器focusin/focusout支持冒泡的兼容后面会详解)

type=(选择器?特别的。delegatetype :特殊。BindType)| |类型;

然后,根据新得到的类型类型(focusin)获取新的特别的

special=jquery。事件。特殊[类型]| | { };

获取的特别的结果为

jquery。每个({ focus : ' focusin ',blur: 'focusout' })、函数(orig,fix){ var attach=0,handler=function(event ) {//模拟冒泡jQuery.event.simulate(fix,event.target,jQuery.event.fix(event),true);};jquery。事件。特殊[修复]={ setup : function(){ if(attach===0){ document。addevent侦听器(orig,handler,true);} },distandown : function(){ if(-attach===0){ document。removeeventlistener(orig,handler,true);} }};});再然后,就是绑定事件,绑定事件实际上就对聚焦输入、聚焦输出做了兼容处理,源码中第一个判断有special.setup.call(…)这段代码,根据上面设置函数可见第一次进入的时候实际上是通过设置函数中的document.addEventListener(orig,handler,true)绑定事件,注意:第一个参数是是orig,因为火狐不支持聚焦输入/聚焦输出所以jQuery使用聚焦/模糊替代来监听事件;注意第三个参数是没错,表示在事件捕获阶段触发事件。

我们知道任何浏览器捕获都是从外层到精确的节点的,所有的focusin事件都会被捕获到,然后执行处理者函数(里面是jQuery.event.simulate函数,源码略)。其他事件绑定则进入如果分支将事件直接绑定到elem上

if(!特别的。设置| |特殊。设置。呼叫(elem,数据,命名空间,eventHandle)==false){ if(elem。addevent listener){ elem。addevent listener(类型,事件句柄,false);} else if(elem。attachevent){ elem。attachevent(' on '类型,事件句柄);} }特殊第二组:mouseenter/mouseleave

//使用鼠标移过/移出和事件时机检测创建鼠标进入/离开事件jquery。每一个({鼠标输入: '鼠标在上面,鼠标离开: '鼠标在外面')},函数(orig,fix){ jquery。事件。特殊[原始]={ delegatetype :修复,bindType:修复,handle:函数(事件){ var ret,target=this,related target=event。相关目标,手柄目标=event.handleObj//对于鼠标输入/离开,当有关系的在目标外面的时候才调用处理程序/参考: 当鼠标离开/进入浏览器窗口的时候是没有相关目标的if(!相关||(相关!==目标!jQuery.contains(target,related)){ event。type=handleobj。原始类型;ret=handleobj。汉德勒。应用(这个,参数);event.type=fix}返回ret } }});需要注意的是只有在鼠标指针穿过被选元素时,才会触发鼠标输入事件。对应老鼠叶这样的话,鼠标输入子元素不会反复触发事件,否则在工业管理学(工业工程)中经常有闪烁情况发生

使用鼠标移过/移出和事件时机检测创建鼠标进入/离开事件有个关键的判断

if(!相关||(相关!==目标!jQuery.contains(目标,相关))其中!jQuery.contains(目标,相关)表示有关系的在目标外面。我们使用图例来解释

我们假设处理的是鼠标输入事件,进入目标。

鼠标从有关系的到目标,很明显有关系的在目标外面,所以当鼠标移动到目标的时候满足条件,调用处理。

现在反过来,很明显有关系的在目标里面,那么鼠标之前就处于鼠标输入状态(意味着之前就进行了鼠标输入处理器处理),避免重复调用当然是不进行任何处理直接返回了。

我们假设处理的是老鼠叶事件,离开目标。

鼠标从目标到相关的,很明显有关系的在目标里面,所以当鼠标移动到有关系的的时候依然么有离开目标,不做处理。

鼠标从目标到相关的,很明显有关系的在目标外面,所以当鼠标移动到有关系的的时候已经离开了目标的范围,做处理。

特别的第三组:提交和变化

主要是工程师协会下使服从不能冒泡的处理

jQuery.event.special.submit主要有一下几个特征

setuppostsdisspachthreadtown

根据添加事件的代码可知添加事件的时候如果符合条件则会调用设置来添加事件

if(!特别的。设置| |特殊。设置。呼叫(elem,数据,命名空间,事件句柄)==false)

jQuery在工程师协会下模拟使服从事件以点击和按键替代,只不过是添加了命名空间来区别和普通点击和按键事件。

setup:函数(){ 0.jQuery.event.add(this,' click ._提交按键_提交',函数(e ) {var elem=e.target,form=jQuery.nodeName(elem,' input' ) || jQuery.nodeName(elem,' button ')?表格未定义;if (form!jQuery ._数据(表单,‘提交气泡’){ jquery。事件。添加(表单,' SubmIt ."提交",函数(事件){活动._ submit _ bubble=true });jQuery ._数据(表单,‘提交气泡’,真);}});},在事件调用过程中(调度)会调用邮政调度来处理

if(特殊。邮政调度){特殊。邮政调度。调用(这个,事件);}后期派遣中调用模仿完成事件处理postDispatch:函数(事件){//如果表单是用户提交的,则将事件冒泡到treeif(事件_ submit _ bubble){ 0删除事件。_提交_气泡;if (this.parentNode!事件。istigger){ jquery。事件。模拟(' submit ',this.parentNode,event,true);}}},拆卸用在删除事件绑定中

工程师协会下变化事件的处理和使服从类似,事件使用激活前替代来监听,处理函数变成了把手,在事件分发(调度)中执行代码

ret=((jquery。事件。特别的。origintype]| | { }).handle || handleObj.handler).apply(matched.elem,args);主要源码如下

jquery。事件。特别的。change={ setup : function(){//rformelems=/^(?输入|选择|文本区域)$/I if(rformelems。测试(这个。nodename)){//IE不会在检查/收音机失焦前触发变化事件;在属性更改后触发它的点击事件//在特殊。改变。处理中会吞掉失焦触发的变化事件。//这里任然会在检查/收音机失焦后触发onchange事件如果(这个。type==' checkbox ' | | this。type==' radio '){ jquery。事件。添加(这是“propertychange ”._变更',函数(事件){ if(event。原创。属性名==='选中'){这._ just _ changed=true } });jQuery.event.add(this,' click ._变更',函数(事件){如果(这个)._刚刚_变了!event.isTrigger ) { this ._ just _ changed=false} //允许触发的模拟更改事件(# 11500)jquery。事件。模拟(‘改变’,这个,事件,真);});}返回false} //事件代理;懒惰模式为后代投入节点添加变化事件处理jQuery.event.add(this,' beforeactivate ._变更',函数{ var elem=e . targetif(rformelems。测试(elem。nodename)!jQuery ._data(elem,“更改气泡”){ jquery。事件。添加(elem,“更改”._变更',函数(事件){ if (this.parentNode!event.isSimulated!事件。istigger){ jquery。事件。模拟(' change ',this.parentNode,event,true);} });jQuery ._data(elem,‘改变气泡’,true);} });},手柄:函数(事件){ var elem=event.target//吞掉本地单选框和复选框的变化事件,我们在上面已经出发了事件如果(这个!==elem | |事件。是模拟|事件。istigger | |(elem。打字!=='radio' elem.type!==' checkbox '){返回事件。手柄。汉德勒。应用(这个,参数);} },}好的,到此,事件系统也告一个段落了,谢谢大家多多支持。

版权声明:jQuery 1.9.1源代码分析系列(X)事件系统的主动触发事件和模拟气泡处理是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。