1、jQuery事件綁定的用法:
$( "elem" ).on( events, [selector], [data], handler );
events:事件名稱,可以是自定義事件名稱
selector:選擇器
data:事件觸發時傳遞給事件處理函數
handler:事件處理函數
2、on方法源碼分析
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var origFn, type;
// 如果types是對象,則說明是傳入了多個事件
if ( typeof types === "object" ) {
// 如果selector不是string,則說明用戶沒有傳入selector
if ( typeof selector !== "string" ) {
// 把selector賦值給data
data = data || selector;
// selector置為undefined
selector = undefined;
}
// 遍歷types對象中的每一個元素,並遞歸調用自身
for ( type in types ) {
this.on( type, selector, data, types[ type ], one );
}
return this;
}
// 如果data和fn都為null,說明用戶只傳了前兩個參數
if ( data == null && fn == null ) {
// 把selector(第二個參數)賦值給fn
fn = selector;
// data和selector置為undefined
data = selector = undefined;
// 如果fn為null,說明用戶傳了三個參數
} else if ( fn == null ) {
// 如果selector的類型是string,說明用戶沒傳data
if ( typeof selector === "string" ) {
// 把data賦值給fn
fn = data;
// 把data置為undefined
data = undefined;
} else {
// 否則的話,說明用戶沒傳selector,而是傳了data,將data賦值給fn
fn = data;
// 將selector賦值給data
data = selector;
// 將selector置為undefined
selector = undefined;
}
}
// 如果用戶傳入的事件處理函數是false值,則將事件處理函數賦值為jQuery內部的returnFalse函數
if ( fn === false ) {
fn = returnFalse;
// 如果用戶沒傳回調函數,返回this,this是啥?返回this干嘛?
} else if ( !fn ) {
return this;
}
// 如果one為1,內部用,暫時沒看到用途
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
// Can use an empty set, since event contains the info
jQuery().off( event );
return origFn.apply( this, arguments );
};
// Use same guid so caller can remove using origFn
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
// 遍歷this對象,調用jQueryevent對象的add方法處理事件
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
},
通過分析on方法的源碼發現,on方法並沒有處理事件相關的任何事情,只是對用戶傳入的參數進行調整,真正處理事件的是event對象
3、首先看看event對象的構造函數都做了什么
jQuery.Event = function( src, props ) {
// 余老板的suggest組件中也用到了這種方法
// 檢測this是不是Event對象,如果不是,new一個Event對象出來,這樣就避免了外部new對象
if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
// 如果有src,並且src有type屬性
if ( src && src.type ) {
// 定義originalEvent屬性並將src賦值給它
this.originalEvent = src;
// 定義type屬性,並將src.type賦值給它
this.type = src.type;
// 定義isDefaultPrevented屬性並通過判斷事件被阻止冒泡為其賦值
this.isDefaultPrevented = ( src.defaultPrevented ||
src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
// 否則,將src賦值給type
} else {
this.type = src;
}
// 如果用戶傳入props,則擴展Event或者覆蓋原有的屬性
if ( props ) {
jQuery.extend( this, props );
}
// 創建一個時間戳??
this.timeStamp = src && src.timeStamp || jQuery.now();
// 給這個Event對象一個標記
this[ jQuery.expando ] = true;
};
看event對象的構造函數發現,構造函數做了一些初始化操作。在看一下event對象封裝的一些方法。
4、jQuery的event對象
jQuery的event對象提供了如下方法和屬性:
{
add : function(){},
remove : function(){},
trigger : function(){},
dispatch : function(){},
handlers : function(){},
fix: function(){},
simulate : function(){},
global : {},
props : {},
fixHooks : {},
keyHooks : {},
mouseHooks : {},
special : {}
}
首先看add方法:
add: function( elem, types, handler, data, selector ) {
var handleObjIn, eventHandle, tmp,
events, t, handleObj,
special, handlers, type, namespaces, origType,
elemData = data_priv.get( elem );
// 涉及到jQuery的另外一個大的方面:緩存機制。或許我應該先看緩存機制的。。。
// 不為text、comment節點綁定數據,直接返回
if ( !elemData ) {
return;
}
// 如果handler是一個有handler屬性或方法的對象,則進行一些轉移賦值操作
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
// 檢查handler是否有一個唯一的id,方便之后查找和刪除
if ( !handler.guid ) {
// 如果沒有就為其設定一個唯一id
handler.guid = jQuery.guid++;
}
// 如果elemData中沒有events對象,則為其定義events屬性並賦值為空對象
if ( !(events = elemData.events) ) {
events = elemData.events = {};
}
// 如果elemData中沒有handle對象
if ( !(eventHandle = elemData.handle) ) {
// 為elemData定義一個handle方法(事件處理函數)
eventHandle = elemData.handle = function( e ) {
// 有點迷糊。。。
return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
// 處理types中傳入的是通過空格分割的多個事件的情況
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
namespaces = ( tmp[2] || "" ).split( "." ).sort();
if ( !type ) {
continue;
}
// 事件是否會改變當前狀態,如果是則使用特殊事件,看event的special屬性。。。
special = jQuery.event.special[ type ] || {};
// 根據是否有selector判斷使用哪種特殊事件(看完特殊事件再過來看這個地方)
type = ( selector ? special.delegateType : special.bindType ) || type;
// 根據新的type獲取新的special
special = jQuery.event.special[ type ] || {};
// 組裝用於特殊事件處理的對象
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 );
// 如果是第一次調用,初始化事件處理隊列(將同一事件的處理函數放入數組中)
if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// 如果獲取特殊事件監聽方法失敗,則使用addEventListener添加事件(拋棄attachEvent了嗎?)
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
}
}
}
// 使用special的add方法進行處理
if ( special.add ) {
// 添加事件
special.add.call( elem, handleObj );
// 設置handleObj中的handler屬性的id
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// ???
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// 現在還沒看到是干啥用的。
jQuery.event.global[ type ] = true;
}
// 將elem清空,等待回收,避免內存泄漏
elem = null;
},