前段時間遇到dom綁定click事件,觸發時會重複執行綁定的函數,探究下jQuery事件綁定機制。基於jQuery-1.10.2。javascript
###原由:(對原始問題的一個抽象案例)html
<!-- lang: html --> <html> <head> <title>test click</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"> </script> </head> <body> <a href="#" id="click1" class="clickc">click1</a> <br> <a href="#" id="click2" class="clickc">click2</a> <br> <a href="#" id="click3" class="clickc">click3</a> <br> <a href="#" id="click4" class="clickc">click4</a> <br> <a href="#" id="click5" class="clickc">click5</a> <br> <hr> <a href="#" id="click" class="target">click</a> <script type="text/javascript"> var count = 0; function clickEvent() { console.log("click --" + count); console.log("---------------"); count ++; } // $('#click1').click(function(){ // $("#click").click(clickEvent); // }) // $('#click2').click(function(){ // $("#click").click(clickEvent); // }) // $('#click3').click(function(){ // $("#click").click(clickEvent); // }) // $('#click4').click(function(){ // $("#click").click(clickEvent); // }) // $('#click5').click(function(){ // $("#click").click(clickEvent); // }) $('.clickc').click(function(){ //$('.target').off(); $('.target').click(clickEvent); // console.log(count); // count++; // var c = document.getElementById('click'); // c.addEventListener('click', clickEvent); }) $('.clickclass').one('click', clickEvent); </script> </body> </html>
####問題描述java
clickEvent
只執行一次【F12, 打開chrome調試工具,找到console】clickEvent
被執行了2 次clickEvent
函數###緣由分析 jQuery的事件綁定機制裏用數組來保存事件,若是對同一元素進行重複綁定,不會覆蓋以前已經綁定的事件,只是把新的綁定事件再push到保存事件的數組中,當事件觸發時就會循環執行數組中的事件。jquery
####jQuery源碼中add
函數進行元素事件的綁定。ajax
<!-- lang: js --> add: function( elem, types, handler, data, selector ) { ...... if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; //事件處理隊列 .... } ...... // 添加到事件列表中, if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } ...... }
####jQuery源碼中dispatch
進行事件處理chrome
<!-- lang: js --> dispatch: function( event ) { .... handlerQueue = jQuery.event.handlers.call( this, event, handlers );//拿到事件隊列 i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); .... } } } return event.result; }
在經過$("").click()
和 $("").on("click",function)
進行事件註冊時,都會調用add
進行事件註冊。api
###解決方法 經過以上分析能夠知道,若是對同一元素進行屢次事件綁定,當觸發事件的時候就會執行事件隊列裏的全部的處理函數。所以能夠有如下幾種方法解決:數組
對元素只進行一次事件綁定。緩存
對元素進行事件綁定前,先調用off()
方法,解決該元素的全部綁定事件處理函數。app
<!-- lang: js -->
$(elem).off(); $(elem).click(func);
使用原生js進行事件註冊,原生的js對事件沒有緩存機制,進行屢次綁定只有最後一次是有效的,後面的會覆蓋前面的事件處理函數。
<!-- lang: js -->
var elem = document.getElementById('click'); elem.addEventListener('click', func);
###【補充】Learning jQuery, 4th Edition p77 在jQuery中,當一個處理函數綁定到事件上時,以前綁定的處理函數依然有效。能夠經過調用 .off()
方法一次性解除全部的綁定。
p78 若是想在事件處理函數第一次觸發後就會解除綁定,能夠用.one()
來綁定函數。
用.one()
來綁定函數,事件處理函數會接着執行。
<!-- lang: js --> one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); } on: function( types, selector, data, fn, /*INTERNAL*/ one ){ .... 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++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }