事件綁定的方式有很多種。使用了jQuery那么原來那種綁定方式(elem.click = function(){...})就不推薦了,原因?
最主要的一個原因是elem.click = fn這種方式只能綁定一個事件處理,多次綁定的只會保留最后一次綁定的結果。
看一下jQuery綁定事件的方式有哪些
jQuery.fn.eventType([[data,] fn])
比如eventType指的是事件類型,比如click: $("#chua").click(fn);
data這個參數一般都不會使用。這種方式事件綁定在("#chua")上,沒有委托事件,和js原生的事件綁定更接近。我們看一下源碼
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +        "mousedown mouseup mousemove mouSEOver mouseout mouseenter mouseleave " +        "change select submit keydown keyPRess keyup error contextmenu").split(" "), function( i, name ) {      //合并15種事件統一增加到jQuery.fn上,內部調用this.on / this.trigger      jQuery.fn[ name ] = function( data, fn ) {      return arguments.length > 0 ?      this.on( name, null, data, fn ) :      //如果不帶參數表示立刻觸發指定事件      this.trigger( name );      };});
jQuery.fn.bind( types[, data], fn )
比如$("#chua").bind("click",fn)。直接將事件綁定到$("#chua")上,沒有委托事件。源碼
bind: function( types, data, fn ) { return this.on( types, null, data, fn );},unbind: function( types, fn ) { return this.off( types, null, fn );}
jQuery.fn.delegate(selector, types[, data], fn)
顧名思義delegate這個函數是用來做事件委托的,將選擇器selector對應的響應處理委托給當前jQuery所匹配的元素。
比如:$(document).delegate('#big',"click",dohander);分析到這里順便分析一下事件委托的處理流程。
當點擊"#big"元素的時候,事件click會冒泡直到document節點;
document綁定了處理事件,這個處理事件會調用到事件分發器dispatch;
dispatch中取出對應事件類型click的所有的委托事件列表handlers;
根據事件源event.target過濾出委托事件列表handlers中每一個元素的selector屬性對應的節點處于事件原和委托節點document之間(包括事件源)的委托事件,保存為handlerQueue;
執行handlerQueue里面的事件處理。
上面是一個大致的流程,后續會詳細分析。先看delegate源碼
delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn );},undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );}
jQuery.fn.one( types[, selector[, data]], fn )
  通過one()函數綁定的事件處理函數都是一次性的,只有首次觸發事件時會執行該事件處理函數。觸發之后,jQuery就會移除當前事件綁定。
比如$("#chua").one("click",fn);為#chua節點綁定一次性的click事件
$(document).one("click","#chua",fn);將#chua的click事件委托給document處理。源碼
one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 );}
jQuery.fn.trigger(type[, data])
jQuery.fn.triggerHandler(type[, data])
trigger觸發jQuery對象所匹配的每一個元素對應type類型的事件。比如$("#chua").trigger("click");
triggeHandler只觸發jQuery對象所匹配的元素中的第一個元素對應的type類型的事件,且不會觸發事件的默認行為。
//立刻觸發jQuery對象內所有元素的指定type的事件trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); });},//立刻觸發jQuery對象內第一個元素的指定type的事件,且不會觸發事件(比如表單提交)的默認行為triggerHandler: function( type, data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); }}
上面分析了那么些個事件綁定,有么有發現他們都是使用.on方式綁定的?這也是為什么提倡統一使用on來綁定的原因(one方式除外)。
jQuery.fn.on( types[, selector[, data]], fn )
.on的事件綁定一半的代碼都實在處理傳遞不同參數的處理,這也是jQuery的口號Write less, do more的代價吧。最終使用jQuery.event.add來綁定事件。
jQuery.event.add綁定事件有幾個比較關鍵的地方:
第一個,使用內部緩存來保存節點elem的事件信息
//獲取緩存數據
elemData = jQuery._data( elem ); ...
//設置緩存數據 if ( !(events = elemData.events) ) { events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { ... }; //將elem作為handle函數的一個特征防止ie非本地事件引起的內存泄露 eventHandle.elem = elem; }
第二個,設置綁定事件信息,特別是指定的選擇器selector、響應處理handler、響應事件類型type、命名空間namespace
// handleObj:設置綁定事件信息。貫穿整個事件處理 handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, // For use in libraries implementing .is(). We use this for POS matching in `select` //"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?://(" + //whitespace + "*((?:-//d)?//d*)" + whitespace + "*//)|)(?=[^-]|$)", "i" ) //用來判斷親密關系 needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn );
第三個,節點的事件列表中,真正的委托事件列表放置在前面,和delegateCount屬性同步,即events.click.length假設為3,events.click.delegateCount假設為2。那么events.click[0]和events.click[1]所指定事件是委托事件。第三個events.click[2]對應的事件不是委托事件,而是節點自身的事件。
//將事件對象handleObj添加到元素的處理列表,委托事件放在前面,委托代理計數遞增 if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); }
源碼和添加事件后的結構上一章已經分析,詳情請點擊查看
綁定有一個公用函數jQuery.fn.on。解綁同樣有一個公用函數jQuery.fn.off
jQuery.fn.off([ types[, selector][, fn]] )
這里的傳參有個比較特殊的情況:當types是瀏覽器事件對象event的時候,表示要去掉(解綁)委托節點上event.selector指定的委托事件
//傳入的參數是事件且綁定了處理函數if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; //types.delegateTarget是事件托管對象 jQuery( types.delegateTarget ).off( //組合jQuery識別的type handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this;}
無論如何最終都是調用jQuery.event.remove函數來解綁事件。
jQuery.fn.off完整的源碼如下

off: function( types, selector, fn ) { var handleObj, type; //傳入的參數是事件且綁定了處理函數 if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; //types.delegateTarget是事件托管對象 jQuery( types.delegateTarget ).off( //組合jQuery識別的type handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); });}
新聞熱點
疑難解答