開發中遇到的jQuery 事件處理機制的問題

前段時間遇到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

  1. 點擊click[1-5]中的任意一個,接着點擊最下面的 click, 這時會在控制檯輸出,此時clickEvent只執行一次【F12, 打開chrome調試工具,找到console】
  2. 點擊click[1-5]中的任意一個,此時點擊最下面的 click,此時在console會有2個輸出,說明 clickEvent 被執行了2 次
  3. 若是不刷新,一直重複上面 的操做,最終的結果就是 點擊幾回click[1-5],點擊click時就會執行幾回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

###解決方法 經過以上分析能夠知道,若是對同一元素進行屢次事件綁定,當觸發事件的時候就會執行事件隊列裏的全部的處理函數。所以能夠有如下幾種方法解決:數組

  1. 對元素只進行一次事件綁定。緩存

  2. 對元素進行事件綁定前,先調用off()方法,解決該元素的全部綁定事件處理函數。app

    <!-- lang: js -->

    $(elem).off(); $(elem).click(func);

  3. 使用原生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 );
});
}
相關文章
相關標籤/搜索