内容简介:(1)trigger("focus") 触发被选元素上的指定事件(focus)以及事件的默认行为(比如表单提交);triggerHandler(xxx) 不会引起事件(比如表单提交)的默认行为
一、 $()
.trigger()和 $()
.triggerHandler() 的作用和区别
(1)trigger("focus") 触发被选元素上的指定事件(focus)以及事件的默认行为(比如表单提交);
triggerHandler(xxx) 不会引起事件(比如表单提交)的默认行为
(2)trigger(xxx) 触发所有匹配元素的指定事件;
triggerHandler(xxx) 只触发第一个匹配元素的指定事件
(3)trigger(xxx) 会冒泡;
triggerHandler(xxx) 不会冒泡
二、 $()
.trigger()
$("#one").on("click",function () { console.log("one被点击了") }) $("#one").trigger('click')
作用:
看 一、(1)
源码:
//触发type事件,data是自定义事件的额外参数 //源码9014行 trigger: function( type, data ) { return this.each( function() { jQuery.event.trigger( type, data, this ); } ); },
解析:
本质是调用的 jQuery.event.trigger()
方法
三、jQuery.event.trigger()
源码:
//源码8850行 //type, data, this trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, //冒泡路径数组 eventPath = [ elem || document ], //判断event是否有'type'属性,有则取event.type,没有则取event type = hasOwn.call( event, "type" ) ? event.type : event, //同上 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; //当前元素 cur = lastElement = tmp = elem = elem || document; //文本内容或者是注释则不触发事件 // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } //由focus/blur转变到focusin/out,现在不触发focus/blur事件 // focus/blur morphs to focusin/out; ensure we're not firing them right now //rfocusMorph:focusin focus|focusout blur if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } //可以不看 if ( type.indexOf( "." ) > -1 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } //onclick,onfocus等等 ontype = type.indexOf( ":" ) < 0 && "on" + type; //event一般是字符串,所以一般是undefined //获取对应type类型的jQuery.event // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : //click,false new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) //onlyHandlers一般为undefined //3 event.isTrigger = onlyHandlers ? 2 : 3; //"" event.namespace = namespaces.join( "." ); //null event.rnamespace = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; //清空event以防它被复用 // Clean up the event in case it is being reused event.result = undefined; //target属性为目标DOM元素 //我们一般取的e.target.value,也正是目标元素的值 if ( !event.target ) { event.target = elem; } //复制data并预先考虑event,创建handler集合 // Clone any incoming data and prepend the event, creating the handler arg list //简单点,就是 data=[event] data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); //赋值有需要特殊处理的type // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } //提前确定事件冒泡的路径 // Determine event propagation path in advance, per W3C events spec (#9951) //冒泡至document,再到window;关注全局的ownerDocument // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { //click bubbleType = special.delegateType || type; //clickclick //如果不是focus/blur的话,获取它的父元素 if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } //for循环的语法(a; b; c) //a在单次循环开始前执行 //b是单次循环的条件(这里即cur存在) //c是单次循环结束后执行 for ( ; cur; cur = cur.parentNode ) { console.log(cur,'cur8967') //将目标元素的祖先元素都push进数组 eventPath.push( cur ); tmp = cur; } //只有当tmp是document时,将window加上 // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } //触发冒泡机制 // Fire handlers on the event path i = 0; //e.stopPropagation()这是阻止冒泡的方法 //isPropagationStopped() 检查是否阻止冒泡了,返回boolean while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { lastElement = cur; event.type = i > 1 ? bubbleType : special.bindType || type; console.log(i,'lastElement8987') // jQuery handler //( dataPriv.get( cur, "events" ) || {} )[ event.type ] // 先判断cur元素的events是否有绑定click //dataPriv.get( cur, "handle" ) //再获取cur元素的click事件处理程序 //获取目标元素的触发事件的事件处理程序 handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && //获取触发事件的处理程序 dataPriv.get( cur, "handle" ); /*让冒泡元素执行handle,这行代码是触发冒泡机制的关键*/ /*在执行click事件的处理程序后,自然就会执行e.stopPropagation(), * 从而让event.isPropagationStopped()=true*/ if ( handle ) { handle.apply( cur, data ); } //接下来处理原生的事件及处理程序 //click为onclick // Native handler handle = ontype && cur[ ontype ]; //如果有绑定原生onclick事件的话 if ( handle && handle.apply && acceptData( cur ) ) { //执行onclick事件的处理程序 event.result = handle.apply( cur, data ); if ( event.result === false ) { //阻止元素的默认行为(如提交表单submit) event.preventDefault(); } } } event.type = type; //如果没有人阻止默认行为的话,现在就阻止 /*比如触发<a>的click事件,但不会跳转*/ // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( ( !special._default || special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { //在目标上,用重复的命名调用原生DOM事件,会在window层面上影响其他元素 // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { //当我们触发FOO事件(如click)时,不要重复触发它的onFOO(onclick)事件 // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; //将jQuery对象的onclick属性置为null //比如<a>就不会去跳转了 if ( tmp ) { elem[ ontype ] = null; } //阻止重复触发同样的事件,因为我们已经把它冒泡了 // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; //如果已经执行阻止冒泡了,则为window添加阻止冒泡的监听 if ( event.isPropagationStopped() ) { lastElement.addEventListener( type, stopPropagationCallback ); } console.log(elem[ type ],'type9053') //执行type事件 elem[ type ](); if ( event.isPropagationStopped() ) { lastElement.removeEventListener( type, stopPropagationCallback ); } jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; },
解析:
(1)trigger()的冒泡机制的实现
在 if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) )
中,通过 eventPath
存储目标元素的祖先元素:
//clickclick //如果不是focus/blur的话,获取它的父元素 if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } //for循环的语法(a; b; c) //a在单次循环开始前执行 //b是单次循环的条件(这里即cur存在) //c是单次循环结束后执行 for ( ; cur; cur = cur.parentNode ) { console.log(cur,'cur8967') //将目标元素的祖先元素都push进数组 eventPath.push( cur ); tmp = cur; } //只有当tmp是document时,将window加上 // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); }
通过 eventPath.push(cur. parentNode)
将冒泡元素装进数组中,并通过 while
循环触发 冒泡机制 :
//触发冒泡机制 // Fire handlers on the event path i = 0; //e.stopPropagation()这是阻止冒泡的方法 //isPropagationStopped() 检查是否阻止冒泡了,返回boolean while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { lastElement = cur; event.type = i > 1 ? bubbleType : special.bindType || type; console.log(i,'lastElement8987') // jQuery handler //( dataPriv.get( cur, "events" ) || {} )[ event.type ] // 先判断cur元素的events是否有绑定click //dataPriv.get( cur, "handle" ) //再获取cur元素的click事件处理程序 //获取目标元素的触发事件的事件处理程序 handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && //获取触发事件的处理程序 dataPriv.get( cur, "handle" ); /*让冒泡元素执行handle,这行代码是触发冒泡机制的关键*/ /*在执行click事件的处理程序后,自然就会执行e.stopPropagation(), * 从而让event.isPropagationStopped()=true*/ if ( handle ) { handle.apply( cur, data ); } //接下来处理原生的事件及处理程序 //click为onclick // Native handler handle = ontype && cur[ ontype ]; //如果有绑定原生onclick事件的话 if ( handle && handle.apply && acceptData( cur ) ) { //执行onclick事件的处理程序 event.result = handle.apply( cur, data ); if ( event.result === false ) { //阻止元素的默认行为(如提交表单submit) event.preventDefault(); } } }
关键代码是 handle.apply( cur, data )
,它用来执行cur元素的事件的处理程序。
(2)通过 e.stopPropagation()
来阻止冒泡的原理:
<body> <script src="jQuery.js"></script> <div id="one">这是one</div> <script> $("#one").click(function(e){ //将handle.apply( cur, data );注释后,冒泡不生效 e.stopPropagation() console.log('one被点击了') }) $("body").click(function(){ console.log('body被点击了') }) //执行trigger()后,会打印one被点击了和body被点击了 $("#one").trigger('click') </script> </body>
① 上面这段代码会先执行 $("#one").trigger('click')
② trigger()里会执行到上面(1)的 handle.apply( cur, data );
,而 handle
会执行 $("#one")
的 click
事件的处理程序:
e.stopPropagation() console.log('one被点击了')
③ e.stopPropagation()
走的是这里:
//event的属性赋值 //源码5749行 jQuery.Event.prototype = { constructor: jQuery.Event, //xxx isPropagationStopped: returnFalse, //false //xxx //xxx //当执行e.stopPropagation()后走这边 //源码5767行 stopPropagation: function() { var e = this.originalEvent; //isPropagationStopped方法返回true this.isPropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopPropagation(); } }, }
最后让 isPropagationStopped()
方法返回 true
④ 注意: $()
.trigger()里的 event
也就是 click
里的 event
,所以会影响到 while
循环地判断, 从而达到阻止冒泡循环的 目的
while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { }
⑤ 为什么说 click
里的 event
是 $()
.trigger()里的 event
?
//event一般是字符串,所以一般是undefined //获取对应type类型的jQuery.event // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : //click,false new jQuery.Event( type, typeof event === "object" && event );
因为 event 是根据 type(click)
类型生成的,所以 trigger
里的 event
的部分属性和 click
的 event
属性相同。
(3)原生js绑定的事件的执行,如 onclick
$("#one").click(function(e){ console.log('one被点击了') }) document.getElementById("one").onclick=function(){ console.log('onclick被点击了') }
还是在 while
循环中:
//接下来处理原生的事件及处理程序 //click为onclick // Native handler handle = ontype && cur[ ontype ]; //如果有绑定原生onclick事件的话 if ( handle && handle.apply && acceptData( cur ) ) { //执行onclick事件的处理程序 event.result = handle.apply( cur, data ); if ( event.result === false ) { //阻止元素的默认行为(如提交表单submit) event.preventDefault(); } }
也就是说:
在冒泡循环机制中,在执行完jQuery绑定的 handler
后,会接着执行原生JS 绑定的 handler
!
(4)rfocusMorph
//匹配focusinfocus或者focusoutblur //源码8872行 var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
(5)jQuery.makeArray()
作用:
用于将一个类似数组的对象转换为真正的数组对象
注意:
类数组对象具有许多数组的属性(例如length属性,[]数组访问运算符等),不过它毕竟不是数组,缺少从数组的原型对象上继承下来的内置方法(例如:pop()、reverse()等)。
源码:
//结果仅供内部使用 // results is for internal usage only //源码442行 makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { //Object()等效于new Object() //先将arr转为对象类型,因为js中的array是Object if ( isArrayLike( Object( arr ) ) ) { //将second合并到first后面 jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { //ret.push(arr) push.call( ret, arr ); } } //返回array return ret; },
① $
.isArrayLike
作用:
判断是不是类数组
源码:
//判断是不是类数组 //源码561行 function isArrayLike( obj ) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE //后两个是兼容性考虑的判断 var length = !!obj && "length" in obj && obj.length, //obj类型 type = toType( obj ); if ( isFunction( obj ) || isWindow( obj ) ) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; }
(6)最后一个if,触发trigger()时,阻止jQuery元素的默认行为
if ( !onlyHandlers && !event.isDefaultPrevented() ){ xxx xxx }
综上,trigger一共做了三件事:
(1)触发冒泡机制
(2)触发原生绑定事件
(3)阻止元素默认行为
最后,附上自己整理的触发 trigger() 的流程图:
(完)
以上所述就是小编给大家介绍的《jQuery源码解析之trigger()》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。