摘要:階段二目標瀏覽器找到監聽器后,就運行該監聽器階段三冒泡目標到祖在事件自下而上到達目標節點的過程中,瀏覽器會檢測不是針對該事件的監聽器用來捕獲事件,并運行非捕獲事件的監聽器。注意下這種情況,是在里的具體實現,即調用一次后,就執行,卸載事件。
前言:
這篇依舊長,請耐心看下去。
一、事件委托
DOM有個事件流特性,所以觸發DOM節點的時候,會經歷3個階段:
(1)階段一:Capturing 事件捕獲(從祖到目標)
在事件自上(document->html->body->xxx)而下到達目標節點的過程中,瀏覽器會檢測 針對該事件的 監聽器(用來捕獲事件),并運行捕獲事件的監聽器。
(2)階段二:Target 目標
瀏覽器找到監聽器后,就運行該監聽器
(3)階段三:Bubbling 冒泡(目標到祖)
在事件自下而上(document->html->body->xxx)到達目標節點的過程中,瀏覽器會檢測不是 針對該事件的 監聽器(用來捕獲事件),并運行非捕獲事件的監聽器。
二、$().click()
作用:
為目標元素綁定點擊事件
源碼:
//這種寫法還第一次見,將所有鼠標事件寫成字符串再換成數組 //再一一綁定到DOM節點上去 //源碼10969行 jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu" ).split( " " ), function( i, name ) { //事件綁定 // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? //如果有參數的話,就用jQuery的on綁定 this.on( name, null, data, fn ) : //否則使用trigger this.trigger( name ); }; } );
解析:
可以看到,jQuery 將所有的鼠標事件都一一列舉了出來,并通過jQuery.fn[ name ] = function( data, fn ) { xxx }
如果有參數,則是綁定事件,調用 on() 方法;
沒有參數,則是調用事件,調用 trigger() 方法( trigger() 放到下篇講 )
三、$().on()
作用:
在被選元素及子元素上添加一個或多個事件處理程序
源碼:
//綁定事件的方法 //源碼5812行 jQuery.fn.extend( { //在被選元素及子元素上添加一個或多個事件處理程序 //$().on("click",function()=<{}) //源碼5817行 on: function( types, selector, data, fn ) { return on( this, types, selector, data, fn ); }, //xxx //xxx })
最終調用的是 jQuery.on() 方法:
//綁定事件的on方法 //源碼5143行 //目標元素,類型(click,mouseenter,focusin,xxx),回調函數function(){xxx} function on( elem, types, selector, data, fn, one ) { var origFn, type; //這邊可以不看 // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { on( elem, type, selector, data, types[ type ], one ); } return elem; } //直接調用$().on()的話會走這邊 if ( data == null && fn == null ) { // ( types, fn ) //fn賦值為selector,即function(){} fn = selector; //再將selector置為undefined //注意這個寫法,連等賦值 data = selector = undefined; } //調用像$().click()的話會走這邊 else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return elem; } //one()走這里 if ( one === 1 ) { //將fn賦給origFn后,再定義fn origFn = fn; fn = function( event ) { //將綁定給目標元素的事件傳給fn, //并通過$().off()卸載掉 // Can use an empty set, since event contains the info jQuery().off( event ); //在origFn運行一次的基礎上,讓origFn調用fn方法,arguments即event return origFn.apply( this, arguments ); }; //讓fn和origFn使用相同的guid,這樣就能移除origFn方法 // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return elem.each( function() { //最終調動$.event.add方法 jQuery.event.add( this, types, fn, data, selector ); } ); }
解析:
可以看到,由于將 bind()、live() 和 delegate() 都合并進 on() 后,on() 里面的情況挺復雜的, data、selector、fn 相互賦值。
注意下 if ( one === 1 ) 這種情況,是 $().one()在on()里的具體實現,即調用一次on()后,就執行jQuery().off( event ),卸載事件。
該方法最終調用 jQuery.event.add( ) 方法
四、jQuery.event.add( )
作用:
為目標元素添加事件
源碼:
//源碼5235行 /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards" addEvent library for many of the ideas. */ jQuery.event = { global: {}, //源碼5241行 //this, types, fn, data, selector add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, //elemData正是目標元素jQuery中的id屬性 //初始值是{} elemData = dataPriv.get( elem ); // Don"t attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } //調用者可以傳入一個自定義數據對象來代替處理程序 // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } //確保不正確的選擇器會拋出異常 // Ensure that invalid selectors throw exceptions at attach time // Evaluate against documentElement in case elem is a non-element node (e.g., document) if ( selector ) { jQuery.find.matchesSelector( documentElement, selector ); } //確保handler有唯一的id // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } //如果事件處理沒有,則置為空對象 // Init the element"s event structure and main handler, if this is the first //在這里,就應經給events賦值了, // 注意這種寫法:賦值的同時,判斷 if ( !( events = elemData.events ) ) { events = elemData.events = {}; } if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { //當在一個頁面卸載后調用事件時,取消jQuery.event.trigger()的第二個事件 // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded //jQuery.event.triggered: undefined //e.type: click/mouseout return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? //讓elem調用jQuery.event.dispatch方法,參數是arguments jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; } //通過空格將多個events分開,一般為一個,如click // Handle multiple events separated by a space types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; //click type = origType = tmp[ 1 ]; //"" namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } //如果event改變了它自己的type,就使用特殊的event handlers // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; //如果選擇器已定義,確定一個特殊event api的type //否則使用默認type // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; //不明白為什么在上面要先寫一遍 // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; //handleObj會傳遞給所有的event handlers // handleObj is passed to all event handlers 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 ); //第一次綁定事件,走這里 // Init the event handler queue if we"re the first if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { //目標元素有addEventListener的話,調用綁定click事件 if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle ); } } } //special的add/handleObj.handler.guidd的初始化處理 if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element"s handler list, delegates in front 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; } }, ... ... }
解析:
可以看到,很多的 if 判斷,都是在初始化對象,最后通過 while 循環,調用目標元素的 addEventListener 事件,也就是說,click()/on() 的本質是 element.addEventListener() 事件,前面一系列的鋪墊,都是在為目標 jQuery 對象添加必要的屬性。
注意寫法 if ( !( events = elemData.events ) ),在賦值的同時,判斷條件
(1)dataPriv
//取唯一id //源碼4361行 var dataPriv = new Data();
在 jQuery 對象中,有唯一id的屬性
$("#one")
elemData = dataPriv.get( elem )
① Data()
//目標元素的jQuery id //源碼4209行 function Data() { this.expando = jQuery.expando + Data.uid++; }
② jQuery.expando
jQuery.extend( { //相當于jQuery為每一個元素取唯一的id ///D/g : 去掉非數字的字符 // Unique for each copy of jQuery on the page //源碼360行 expando: "jQuery" + ( version + Math.random() ).replace( /D/g, "" ), ... ... })
③ Math.random()
偽隨機,到小數點后16位
expando: "jQuery" + ( version + Math.random() ).replace( /D/g, "" ),
可以看到 jQuery 的 id 是由 jQuery + 版本號+ Math.random() 生成的
關于 Math.random() 是如何生成偽隨機數的請看:https://www.zhihu.com/question/22818104
(2)rtypenamespace
var rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, //事件類型的命名空間 //舉例:var arr1 = "click.aaa.bbb".match(rtypenamespace); //console.log(arr1);//["click.aaa.bbb", "click", "aaa.bbb", index: 0, input: "click.aaa.bbb"] //源碼5131行 rtypenamespace = /^([^.]*)(?:.(.+)|)/;
綜上,綁定事件的本質即調用element.addEventListener()方法,但 jQuery 有太多的情況需要考慮了。
(完)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109877.html
摘要:源碼源碼行被點擊了點擊了,即委托的事件被點擊了優先添加委托,再添加其他即委托在上的事件數量在下標為的位置插入委托事件解析可以看到,是優先添加委托事件,再添加自身事件,觸發事件的時候也是按這個順序。 showImg(https://segmentfault.com/img/remote/1460000019419722); 前言:請先回顧下我之前寫的一篇文章:JavaScript之事件委...
摘要:一和的作用和區別觸發被選元素上的指定事件以及事件的默認行為比如表單提交不會引起事件比如表單提交的默認行為觸發所有匹配元素的指定事件只觸發第一個匹配元素的指定事件會冒泡不會冒泡二被點擊了作用看一源碼觸發事件,是自定義事件的額外參數源碼行解析本 showImg(https://segmentfault.com/img/remote/1460000019375685); 一、$().trig...
摘要:一起源方法最終是用綁定事件的而方法正是等于二作用觸發綁定的事件的處理程序源碼源碼行即原生觸發事件的處理程序修正對象獲取事件的處理程序集合,結構如下從數據緩存中獲取事件處理集合即目標元素委托目標這段代碼壓根不會執行,因為全局搜索沒找到結構 showImg(https://segmentfault.com/img/remote/1460000019464031); 一、起源jQuery.e...
閱讀 2568·2021-11-22 13:53
閱讀 4069·2021-09-28 09:47
閱讀 858·2021-09-22 15:33
閱讀 809·2020-12-03 17:17
閱讀 3315·2019-08-30 13:13
閱讀 2121·2019-08-29 16:09
閱讀 1176·2019-08-29 12:24
閱讀 2452·2019-08-28 18:14