为什么使用JQuery on()API执行事件处理程序不止一次?

时间:2013-07-06 08:01:54

标签: jquery events

当我使用on()来监听事件时,我发现事件处理程序被多次调用。代码如下:

HTML:

<div id="container">
<div>
    <div><input type="text"/></div>        
</div>    
</div>

JS:

var $div = $('#container'), $input = $('input');

$div.on('validate', 'div', function() {
    console.log('div');
});

$input.blur(function() {
    $(this).trigger('validate');
});

Here is the updated DEMO

  • 然后调用'validate'处理程序两次。
  • 当我深入研究JQuery时 源代码,我发现JQuery会找到事件的父节点 目标并检查匹配或未匹配的seletor。
  • 如果匹配选择器, 处理程序队列将再次被推送到handleobj。在里面 例如,在事件目标和侦听器元素div #contianer之间, 有两个等级div。
  • 结果,处理程序队列有两个 事件处理程序,它们是相同的事件处理函数实例。

我的问题是:如何正确使用选择器以防止多次调用事件处理程序?谢谢。

我想回答这个问题,我们应该仔细阅读JQuery中的相关源代码:

// Determine handlers that should run if there are delegated events
        // Avoid non-left-click bubbling in Firefox (#3861)
        if ( delegateCount && !(event.button && event.type === "click") ) {

            // Pregenerate a single jQuery object for reuse with .is()
            jqcur = jQuery(this);
            jqcur.context = this.ownerDocument || this;

            for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {

                // Don't process events on disabled elements (#6911, #8165)
                if ( cur.disabled !== true ) {
                    selMatch = {};
                    matches = [];
                    jqcur[0] = cur;
                    for ( i = 0; i < delegateCount; i++ ) {
                        handleObj = handlers[ i ];
                        sel = handleObj.selector;

                        if ( selMatch[ sel ] === undefined ) {
                            selMatch[ sel ] = (
                                handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
                            );
                        }
                        if ( selMatch[ sel ] ) {
                            matches.push( handleObj );
                        }
                    }
                    if ( matches.length ) {
                        handlerQueue.push({ elem: cur, matches: matches });
                    }
                }
            }
        }

3 个答案:

答案 0 :(得分:0)

  

$ input.blur(function(){$(this).trigger('validate');});'只有输入触发器   验证事件。因此,我认为这并不意味着“为DOM上的每个元素工作onblur

这是错误的,因为触发器冒出DOM树,并为父元素触发相同的事件。

From DOCS

  

从jQuery 1.3开始,.trigger()ed事件冒泡了DOM树;事件处理程序可以通过从处理程序返回false或对传递给事件的事件对象调用.stopPropagation()方法来停止冒泡

因此div#container内有两个div,

$input.blur(function() {$(this).trigger('validate');});

也会触发这两个div。

以下示例显示哪个元素为currentTarget

http://jsfiddle.net/egcVb/4/

$div.on('validate', 'div', function(e) {
    console.log(e.currentTarget);
});

如果你想停止冒泡,只需从该事件中返回false。

http://jsfiddle.net/egcVb/5/

$div.on('validate', 'div', function(e) {
    console.log(e.currentTarget);
    return false;
});

答案 1 :(得分:0)

这意味着:在$ div上绑定'validate'事件,但也为每个div后代包含它。

$div.on('validate', 'div', function() {
    console.log('div');
});

请参阅jQuery on,参数selector

  

用于过滤所选元素后代的选择器字符串   触发事件。如果选择器为null或省略,则为事件   当它到达所选元素时总是被触发。

使用此:

$div.on('validate', function() {
    console.log('div');
});

......你只会处理一件事。

答案 2 :(得分:0)

如果您有充分的理由将事件处理程序绑定到 div 而不是 input ,则可以将事件处理程序更改为如下所示:

$div.on('validate', '*', function(event) {
    var isRelevant = false;
    $(this).children().each(function(){
        if (this === event.target){
            isRelevant = true;
        }
    });

    if (isRelevant){
        console.log('div');
    }
});

此代码段会检查事件发出的 event.target doc)是否是事件传播到的元素的直接子元素之一。 对于内部div, isRelevant 只会是 true

您也可以在我的例子中找到它:http://jsfiddle.net/BwseN/