JQuery事件模型并防止重复处理程序

时间:2010-02-01 21:49:49

标签: jquery events triggers bind javascript

我想再次使用$(“divid”)。load(...)将包含自己脚本的页面加载到div中。我面临的问题与事件有关。假设我们从父页面和我们绑定的加载页面(“猴子”)触发(“猴子”)并且只是做一个警报(“猴子绑定”)。如果多次调用相同的load方法,则会多次调用bind。现在我可以在绑定它之前取消绑定它,或者在绑定之前检查处理程序的数量,然后不绑定它以防止这种情况。这两个选项都不是可扩展的,如果我以后想要绑定到另一个“子页面”(加载到div中的页面)中的触发器。

我理想的做法是检查我要添加的处理程序是否已经存在,但我仍然想要使用匿名处理程序...(我想的最后一个请求有点问)。目前我有一个解决方法,使用预定义/命名方法,然后在绑定之前检查它。

// Found this on StackOverflow
function getFunctionName(fn)
{
 var rgx = /^function\s+([^\(\s]+)/
 var matches = rgx.exec(fn.toString());
 return matches ? matches[1] : "(anonymous)"
}

function HandlerExists(triggerName, handlerName) {
        exists = false;
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if(event !== undefined)
            {
                $.each(event, function(i, handler) {
                    alert(handlerName);
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }

这是一种非常粗暴的方式,我觉得,但似乎工作。我只是在绑定之前执行以下操作:

if (!HandlerExists("test", "theMethod")) {
    $(document).bind("test", theMethod);
}

有没有人有更优雅的解决方案?例如,有没有办法检查特定的脚本是否已加载?所以我可以使用getScript()在第一次加载时从子页面加载js,然后根本不加载它在后续加载(并且只是触发一个触发器,它将由他预先存在的js处理)。

5 个答案:

答案 0 :(得分:50)

使用jQuery的事件命名空间防止重复绑定

实际上有几种不同的方法可以防止重复。一种是在unbind中传递原始处理程序,但如果它是一个副本而不是在内存中的同一空间中它不会解除绑定,另一种流行的方式(使用命名空间)是一种更加确定的实现方式。

这是事件的常见问题。因此,我将对jQuery事件进行一些解释,并使用命名空间来防止重复绑定。



答案:(简短而直截了当)


// bind handler normally
$('#myElement').bind('myEvent', myMainHandler);

// bind another using namespace
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// unbind the unique and rebind the unique
$('#myElement').unbind('myEvent.myNamespace').bind('myEvent.myNamespace', myUniqueHandler);
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// trigger event
$('#myElement').trigger('myEvent');

// output
myMainHandler() // fires once!
myUniqueHandler() // fires once!



答案示例:(完整详细说明)


首先让我们创建一个绑定的示例元素。我们将使用id为#button的按钮。然后创建3个可以用作处理程序的函数来绑定事件:

函数exampleOne()我们将通过单击进行绑定。 function exampleTwo()我们将绑定到click的命名空间。 函数exampleThree()我们将绑定到click的namepsace,但是解绑并绑定多次而不删除其他绑定,这样可以防止重复绑定,同时不删除任​​何其他绑定方法。

示例开始:(创建要绑定的元素以及一些方法作为我们的处理程序)

<button id="button">click me!</button>


// create the example methods for our handlers
function exampleOne(){ alert('One fired!'); }
function exampleTwo(){ alert('Two fired!'); }
function exampleThree(){ alert('Three fired!'); }

绑定exampleOne点击:

$('#button').bind('click', exampleOne); // bind example one to "click" 

现在,如果用户点击按钮或拨打$(&#39;#按钮&#39;)。触发(&#39;点击&#39;),您将收到警报&#34; One Fired!&#34 ;;

将exampleTwo绑定到click的命名空间:&#34; name是任意的,我们将使用myNamespace2&#34;

$('#button').bind('click.myNamespace2', exampleTwo);

关于这个很酷的事情是,我们可以触发&#34;点击&#34;这将触发exampleOne()和exampleTwo(),或者我们可以触发&#34; click.myNamespace2&#34;这只会触发exampleTwo()

将exampleThree绑定到click的命名空间:&#34;再次,name是任意的,只要它与exampleTwo的命名空间不同,我们将使用myNamespace3&#34 ;

$('#button').bind('click.myNamespace3', exampleThree);

现在,如果点击&#39; get触发所有三个示例方法都会被触发,或者我们可以定位特定的命名空间。

全力以赴防止重复

如果我们继续像这样继续绑定exampleThree():

$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').bind('click.myNamespace3', exampleThree);

他们会被解雇三次,因为每次调用bind时都会将其添加到事件数组中。所以,真的很简单。只需在绑定之前取消绑定该命名空间,如下所示:

$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);

如果触发了click函数,则exampleOne(),exampleTwo()和exampleThree()只会被触发一次。

将所有内容整合在一个简单的函数中:

var myClickBinding = function(jqEle, handler, namespace){
    if(namespace == undefined){
        jqEle.bind('click', handler);
    }else{
        jqEle.unbind('click.'+namespace).bind('click.'+namespace, handler);
    }
}   

<强>要点:

jQuery事件命名空间允许绑定到主事件,但也允许创建和清除子命名空间,而不会影响同级命名空间或父级命名空间,这些命名空间具有极少的创造性思维,可以防止重复绑定。

有关进一步说明:http://api.jquery.com/event.namespace/

答案 1 :(得分:23)

来自

的优秀答案

bind event only once

<强> copyPastedInfo

如果您可以申请,可能需要查看event.preventDefaulttevent.stopPropagation 或者在

之类的方法中每次取消绑定和绑定
function someMethod()
{
  $(obj).off('click').on('click', function {});
}

答案 2 :(得分:7)

嗯,使用one()怎么样? http://api.jquery.com/one/

或者我完全误解了你?

答案 3 :(得分:6)

这不是一个完整的答案,但我很快就找到了如何以最小的更改修复我的所有代码。在我的“基础”js类中(这是一个在每个页面上用于按类名等标准按钮连接的对象)我添加了以下方法来检查欺骗处理程序:

BindOnce: function(triggerName, fn) {
    function handlerExists(triggerName, theHandler) {
        function getFunctionName(fn) {
            var rgx = /^function\s+([^\(\s]+)/
            var matches = rgx.exec(fn.toString());
            return matches ? matches[1] : "(anonymous)"
        }
        exists = false;
        var handlerName = getFunctionName(theHandler);
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if (event !== undefined) {
                $.each(event, function(i, handler) {
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }
    if (!handlerExists(triggerName, fn)) {
        $(document).bind(triggerName, fn);
    }
},

然后我只是调用它而不是bind方法!

$.mystuff.BindOnce("TheTrigger", OnTriggered)

请注意,你不能在这里使用anon方法,因为它们只会被称为1,2,3等,并且检查dupes的唯一方法是使用方法上的toString(),这在一个复杂的应用程序

答案 4 :(得分:1)

大约一百年太晚了,但是如果你不使用匿名函数,你可以通过首先使用unbind来更简单地做到这一点:

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
        var oneHandler = (function () {
             HANDLER CODE
        });

    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

此实现适用于Firefox 4,但不适用于许多其他浏览器 - 因为每次都会创建新的处理程序变量,因此unbind无法找到解除绑定的匹配处理程序。将处理程序移动到父作用域中可以解决问题:即

var oneHandler = (function () {
    HANDLER CODE
});

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

据推测,Firefox正在做一些聪明的优化,这意味着处理程序在某个地方保持为常量变量,因为它永远不会改变。