jQueryソース分析のjQuery.event.special八問

17658 ワード

ブログ参照:クリックしてリンクを開く
まずこの図を見る
質問1:もし事件に泡が出なかったらどうしますか?
 $("#n1").on("focus",function(e)
	{
	  console.log("n1 focus!");
	});
	$("#n3").on("focus",function(e)
	//      focus,  focus   ,     n1    !
	{
	  console.log("n3 focus!");
	});
	jQuery.event.trigger("focus",{name:"qinliang"},$("#n3")[0]);
このときn 1という親要素のfocusはまったく呼び出されません.focus自体が泡を立てないからです.
            focus: {
			// Fire native event if possible so blur/focus sequence is correct
			trigger: function() {
				if ( this !== safeActiveElement() && this.focus ) {
					try {
						this.focus();//          function(){[native code]}!
						return false;
					} catch ( e ) {
						// Support: IE<9
						// If we error on focus to hidden element (#1486, #12518),
						// let .trigger() run the handlers
					}
				}
			},
			delegateType: "focusin"//      focusin,focusin    !
		}
でjQuery.event.triggerはどのようにして親要素のfocusメソッドをトリガーしないのですか?
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
			return;
		}
この泡が出ないfocusのtrigger法をfalseに戻すと、jQueyr.event.triggerは直接戻りました.targetの親要素のセットを取得して、セット内の各要素のfocusメソッドを呼び出すことはありません.
 $("#n1").on("focusin",function(e)
	{
	  console.log("n1 focusin invoked!");
	});
	$("#n3").on("focus",function(e)
	//      focus,  focus   ,     n1    !
	//          focusin,       ,  JS  
	//      focus,  focusin,  focusin   ,     focusin   !
	{
	  console.log("n3 focus!");
	});
	jQuery.event.trigger("focus",{name:"qinliang"},$("#n3")[0]);

focusinは要素がフォーカスを取得したことを表し、このイベントが上に泡を立てるので、親要素focusinはjQueryの処理ではなくjsメカニズムから呼び出されます!
質問2:ではdelegateTypeの役割は何ですか.
    $("#n1").on("focus","#n3",function(e)
	//       focusin  ,      ,  jQuery        delegateType!
	{
	  console.log("n1 focusin invoked!");
	});
	jQuery.event.trigger("focus",{name:"qinliang"},$("#n3")[0]);
このときn 1は代理オブジェクトなので、focusのdelegateType、つまりfocusinが与えられている点に注意してください.彼はどうやってやったのか、jQueryを見てみましょう.event.addメソッド
special = jQuery.event.special[ type ] || {};
      //    selector    DOM     selector         ,    delegateType!
     // If selector defined, determine special event api type, otherwise given type
type = ( selector ? special.delegateType : special.bindType ) || type;

このコードは、イベントをバインドするときにselectorを指定すると、現在の要素がエージェントオブジェクトであることを示すので、彼がバインドしたイベントはdelegateTypeであり、ここでfocusinである.これはなぜn 1のfocusが実行されるのかを示すが、このメカニズムはjQuery内部の処理であり、JS自体のメカニズムではないからである.質問3:blurメソッドも上のメカニズムではないでしょうか.
blur: {
	trigger: function() {
				if ( this === safeActiveElement() && this.blur ) {
					this.blur();//this  DOM,      data=[event,data]!
					return false;//                   focusOut, JS     
				}
			},
			delegateType: "focusout"
		}
同様に、要素登録blurを呼び出すとselectorが含まれていればdelegateTypeがfocusoutになります!
 $("#n1").on("blur","#n3",function(e)
	//       focusin  ,      ,  jQuery        delegateType!
	{
	  console.log("n1 blur invoked!");
	});
	jQuery.event.trigger("blur",{name:"qinliang"},$("#n3")[0]);
このメカニズムは、JS自体のメカニズムではなく、jQuery内部の処理に由来する.
質問3:clickも特殊なイベントですが、なぜですか.
  $("#n1").on("click",function(e)
	//click      ,        n1  !
	{
	  console.log("n1 blur invoked!");
	});
	jQuery.event.trigger("click",{name:"qinliang"},$("#n3")[0]);
clickは自分で泡が立ちますが、checkboxでtriggerを呼び出す場合は手動でclickを呼び出さなければなりません.そうしないと選択されません.彼が泡を立てるからこそ、親要素を1つずつ取得して親要素のclickイベントを呼び出す必要はありません.同時にハイパーリンクされたclickイベントはデフォルトの動作を実行しません!
               // For checkbox, fire native event so checked state will be right
			trigger: function() {
				if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
					this.click();//       click,  checkbox     !
					return false;
					//return false             click  ,       ,       
					//  JS          !
				}
			},
			// For cross-browser consistency, don't fire native .click() on links
			//  a   click       !
			_default: function( event ) {
				return jQuery.nodeName( event.target, "a" );
			}
ハイパーリンクのclickはデフォルトのリンクを開きません
	jQuery.event.trigger("click",{name:"qinliang"},$("#n3")[0]);
問題4:loadイベントはなぜ特殊なイベントなのか.
私达はいつも1つの要素のload事件を绝えずすべての父の要素に伝達することができなくて、1つのピクチャーをロードしてそんなにloadを何回要して、だからload事件は泡を立てさせません
<div id="father">
     <img src="images/1.png" id="img"/>
 </div>
JS部
 $("#father").on("load",function()//    load  ,    
	{
	  console.log("log");
	});
	jQuery.event.trigger("load",{name:"qinliang"},$("img")[0]);
質問5:beforeunloadは何をしていますか?
                beforeunload: {
			//dispatch    ,      DOM,       
			postDispatch: function( event ) {
				// Support: Firefox 20+
				// Firefox doesn't alert if the returnValue field is not set.
				//                 ,   JS     returnValue  ,   IE   !
				if ( event.result !== undefined && event.originalEvent ) {
					event.originalEvent.returnValue = event.result;
				}
			}
		}
問題6:mouseover,mouseenterなどの処理処理?
  $("#father").mouseenter(function()
	  {
	    console.log("father mouseenter");
	  });
	  var expando=jQuery.expando;
	  var key=$("#father")[0][expando];
	  var walhouse=jQuery.cache;
	  var data=walhouse[key];
	  console.log(data);//       ,events       mouseover    mouseenter!
ですのでjQueryはmouseenter,mouseleaveなどの泡が立たないイベントをすべてmouseoutなどの泡が立つイベントに置き換えます
  $(document).on("mouseenter",function()
	   {
	     console.log("document!");
	   });
      $("#father").on("mouseenter","#child",function(arg)
	  {
        //mouseout     ,    target  ,    event      
		//           !
		console.log(arg);
	  });
	  var expando=jQuery.expando;
	  var key=$("#father")[0][expando];
	  var walhouse=jQuery.cache;
	  var data=walhouse[key];
	  console.log(data);//       ,events       mouseover    mouseenter!
コンテキストはcurrentTargetオブジェクトであり、最初のパラメータはeventオブジェクトであり、このオブジェクトにはこのイベントのすべての情報が含まれています.また、最初にバインドされたイベントが泡を出さないイベントにバインドされるため、
だからエージェントイベントは必ず呼び出されます!
// Create mouseenter/leave events using mouseover/out and event-time checks
jQuery.each({
	mouseenter: "mouseover",
	mouseleave: "mouseout",
	pointerenter: "pointerover",
	pointerleave: "pointerout"
}, function( orig, fix ) {
	jQuery.event.special[ orig ] = {
		//       ,               
		delegateType: fix,
		bindType: fix,
		//bindType  mouseover       ,    on    mouseenter        mouseover!
		handle: function( event ) {
			var ret,
				target = this,//  :          ,     this              !
				related = event.relatedTarget,//  relatedTarget!
				handleObj = event.handleObj;
			// For mousenter/leave call the handler if related is outside the target.
			// NB: No relatedTarget if the mouse left/entered the browser window
			//              relatedTarget!
			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
				event.type = handleObj.origType;
				//           ,        !
				ret = handleObj.handler.apply( this, arguments );
				event.type = fix;
			}
			return ret;
		}
	};
});
問題7:checkboxとradioはIEの中で泡が立たないで、それではどのように処理して、どのようにIEに対して解決しますか?
 <div id="father">
         :<input type="checkbox" value=" " name="qin"/>
	     :<input type="checkbox" value=" " name="qin"/>
 </div>
father要素はサブ要素のchangeイベントをキャプチャできません
 document.getElementById("father").onchange=function()
	 {
	   alert("change!");//IE radio checkbox     !
}
jQuery.event.addメソッドでは、setupがない場合、JSでよく使用されるイベントバインディングに従ってaddEventListenerまたはattachEventを完了します.
// Only use addEventListener/attachEvent if the special events handler returns false
		if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {//IE checkbox       false
			// Bind the global event handler to the element
			if ( elem.addEventListener ) {
				elem.addEventListener( type, eventHandle, false );
			} else if ( elem.attachEvent ) {
						elem.attachEvent( "on" + type, eventHandle );
			}
	}
setupではコンテキストがバインドイベントのDOM、すなわちエージェントオブジェクトであり、最初のパラメータは入力dataであり、2番目のパラメータはネーミングスペースであり、3番目のパラメータは汎用コールバック関数であることがわかります(
注意:ここでIEのcheckboxは泡を立てずにfalseに戻ったので、共通の関数でバインドされたイベントですが、彼は依然として次のカスタムイベントを持っています.
IEに対する処理はカスタムイベントを追加することであることがわかる.
if (!support.changeBubbles ) {//  IE checkox radio      
	jQuery.event.special.change = {
		//              IE       !
		setup: function() {
			//var rformElems = /^(?:input|select|textarea)$/i,
			if ( rformElems.test( this.nodeName ) ) {
				// IE doesn't fire change on a check/radio until blur; trigger it on click
				// after a propertychange. Eat the blur-change in special.change.handle.
				// This still fires onchange a second time for check/radio after blur.
				if ( this.type === "checkbox" || this.type === "radio" ) {
					//       propertychange._change,      propertychange  
					jQuery.event.add( this, "propertychange._change", function( event ) {
						if ( event.originalEvent.propertyName === "checked" ) {//
							this._just_changed = true;
						}
					});
					//       click._change,     click!
					jQuery.event.add( this, "click._change", function( event ) {
						if ( this._just_changed && !event.isTrigger ) {
							this._just_changed = false;
						}
						// Allow triggered, simulated change events (#11500)
						jQuery.event.simulate( "change", this, event, true );
					});
				}
				//       ,          !
				return false;
			}
			// Delegated event; lazy-add a change handler on descendant inputs
			//       beforeactivate._change
			jQuery.event.add( this, "beforeactivate._change", function( e ) {
				var elem = e.target;
				if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
					jQuery.event.add( elem, "change._change", function( event ) {
						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
							jQuery.event.simulate( "change", this.parentNode, event, true );
						}
					});
					jQuery._data( elem, "changeBubbles", true );
				}
			});
		},
        //      !
		handle: function( event ) {
			var elem = event.target;//          ,    handle            !
			// Swallow native change events from checkbox/radio, we already triggered them above
			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
				return event.handleObj.handler.apply( this, arguments );
			}
		},
        //       ,  ._change   ,         !
		teardown: function() {
			jQuery.event.remove( this, "._change" );
			return !rformElems.test( this.nodeName );
		}
	};
}
この図を通じて、IEにとって、propertychangeとclickの2つのカスタムイベントが追加されていることがわかります.私たち自身が追加したchangeイベントを加えると、要素内部に3つのイベントが保存されます!
checkboxとradioでなければ、バブルがサポートされていない場合、beforeactivateイベントというイベントしか追加されません.
この事件はjQueryでevent.triggerでどのように呼び出されたか
         while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
			event.type = i > 1 ?
				bubbleType :
				special.bindType || type;
			// jQuery handler
			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );//trigger          
			if ( handle ) {
				handle.apply( cur, data );
			}
			// Native handler
			handle = ontype && cur[ ontype ];
			if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
				event.result = handle.apply( cur, data );
				if ( event.result === false ) {
					event.preventDefault();
				}
			}
		}
このようなカスタマイズされたイベントまたは特殊なイベントは、targetからこのイベントを階層的に呼び出し始め、親要素のtypeおよびontypeイベントを階層的に呼び出し、親要素の集合、例えば[input#man,div#father,body,html,document,Window]
したがって,同類のイベントはすべて呼び出され,バブルが実現した.
問題8:focusinBubbleは解決できますが、デフォルトではjQueryが追加されます.event.special["focusin/focusout"]?
// Create "bubbling" focus and blur events
if ( !support.focusinBubbles ) {
	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
		// Attach a single capturing handler on the document while someone wants focusin/focusout
		var handler = function( event ) {
				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
			};

		jQuery.event.special[ fix ] = {
			setup: function() {
				//   document      focusin/focusout,          focusin!
				var doc = this.ownerDocument || this,
					attaches = jQuery._data( doc, fix );

				if ( !attaches ) {
					doc.addEventListener( orig, handler, true );
				}
				jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
			},
			teardown: function() {
				var doc = this.ownerDocument || this,
					attaches = jQuery._data( doc, fix ) - 1;

				if ( !attaches ) {
					doc.removeEventListener( orig, handler, true );
					jQuery._removeData( doc, fix );
				} else {
					jQuery._data( doc, fix, attaches );
				}
			}
		};
	});
}

質問9:IEに対してsubmitエージェントイベントを修正しますか?
//  IE  submit     
// IE submit delegation
if ( !support.submitBubbles ) {
	jQuery.event.special.submit = {
		setup: function() {
			// Only need this for delegated form submit events
			if ( jQuery.nodeName( this, "form" ) ) {
				return false;
			}
			// Lazy-add a submit handler when a descendant form may potentially be submitted
			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
				// Node name check avoids a VML-related crash in IE (#9807)
				var elem = e.target,
					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
				if ( form && !jQuery._data( form, "submitBubbles" ) ) {
					jQuery.event.add( form, "submit._submit", function( event ) {
						event._submit_bubble = true;
					});
					jQuery._data( form, "submitBubbles", true );
				}
			});
			// return undefined since we don't need an event listener
		},
		postDispatch: function( event ) {
			// If form was submitted by the user, bubble the event up the tree
			if ( event._submit_bubble ) {
				delete event._submit_bubble;
				if ( this.parentNode && !event.isTrigger ) {
					jQuery.event.simulate( "submit", this.parentNode, event, true );
				}
			}
		},
		teardown: function() {
			// Only need this for delegated form submit events
			if ( jQuery.nodeName( this, "form" ) ) {
				return false;
			}

			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
			jQuery.event.remove( this, "._submit" );
		}
	};
}

まとめ:
(1)document.ActiveElementは現在フォーカスを取得している要素を取得できます.選択していない場合は、activeElementはbodyですが、すべてのブラウザがサポートされていますか?
(2)focus,blur,submit,change,mouseenter,mouseleave,pointerenter,pointerleaveでもエージェントされることがわかる!
(3)focusとblurの最大の問題は彼らが泡を立てないことであるため、IEのfocusinとfocusoutはDOM 3級イベントによって標準として採用される.そこでここのfocusはfocusinでエージェントを行い、blurはfocusoutエージェントを使います!OperaはDOMFOcusInとDOMFOcusOUTを使っています!
(4)マウスイベントについてはmouseenter,mouseleave以外は泡が立つので,jQUeryソースコードではmouseover,mouseoutをdelegateTypeとし,さらにdelegateでバインドした場合にもイベントエージェントを行うことができる!注意:mouseoutは別の要素に移動するとトリガーされますが、別の要素はその要素のサブ要素または外部要素です.同時にmouseoverも要素を移動する際にトリガーされ、移動する要素は要素のサブ要素であってもよい(スペース部分もサブ要素であり、この部分のnodeTypeは3であることに注意).mouseout見クリック開くリンクmouseover見クリック開くリンク
(5)pointerenter,pointerleaveに関する知識クリックしてリンクを開く
(6)IEのsubmitイベントもエージェントできるが,フォームのsubmit()メソッドでフォームをコミットしてもsubmitイベントはトリガーされないので,そのメソッドを呼び出す前にデータの検証を行うことに注意する.
(7)IEのchangeイベントはエージェントを行う.changeイベントは異なるフォームコントロールに対してトリガーされる回数が異なり、inputとtextareaに対してフォーカス獲得からフォーカス喪失までvalue値が変化するとchangeがトリガーされ、selectに対してユーザーが異なるオプションを選択するとchangeがトリガーされ、言い換えればフォーカスを失わずにchangeイベントがトリガーされます!
(8)これらの方法では、関数return falseを呼び出すとデフォルトの動作だけでなく、バブルも阻止されます.dispatchのソースコードを添付します.
 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )  
                            .apply( matched.elem, args );  
        //  undefined,          handle  !    undefined!  
               // alert(ret);  
                //  undefined,result     !  
                //alert(event.result);  
                    if ( ret !== undefined ) {  
                        if ( (event.result = ret) === false ) {  
                            event.preventDefault();  
                            event.stopPropagation();  
                        }  
                    }  
//          ,         ,   checkbox       :
function handler(event){
return false;
}
$("input").click(handler);