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

时间:2021-08-31 03:32:33

Once again I want to load a page which contains its own script into a div using $("divid").load(...). The problem I face is related to events. Let's say we trigger("monkey") from the parent page and on the loaded page we bind("monkey") and just do an alert("monkey bound"). If the same load method is called multiple times, the bind is called multiple times. Now I could just unbind it before I bind it, or check the number of handlers before the bind and then not bind it to prevent this. Neither option is scalable as what if I later want to bind to that trigger in another "sub page" (a page loaded into a div).

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

What I ideally want to do then is check if the handler I am about to add already exists, but I still WANT to use anonymous handlers... (asking a bit much with that last request I think). Currently I have a workaround by using pre-defined/named methods and then checking this before the bind.

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

// Found this on *
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;
    }

This is a pretty crude way of going about it I feel, but appears to work. I just do the following before the bind as follows:

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

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

Does anyone have a more elegant solution? for instance, is there any way to check a particular script is loaded? so I could use getScript() to load the js from the child page on first load, and then simply not load it on subsequent loads (and just fire a trigger which would be handled by he preexisting js)..

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

5 个解决方案

#1


49  

Prevent duplicate binding using jQuery's event namespace

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

There are actually a couple different ways of preventing duplicate. One is just passing the original handler in the unbind, BUT if it is a copy and not in the same space in memory it will not unbind, the other popular way (using namespaces) is a more certain way of achieving this.

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

This is a common issue with events. So I'll explain a little on the jQuery events and using namespace to prevent duplicate bindings.

这是事件的常见问题。所以我将解释一下jQuery事件并使用命名空间来防止重复绑定。



ANSWER: (Short and straight to the point)

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


// 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!



EXAMPLE OF ANSWER: (Full detailed explanation)

答案示例:(详细说明)


First let's create an example element to bind to. We will use a button with the id of #button. Then make 3 functions that can and will be used as the handlers to get bound to the event:

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

function exampleOne() we will bind with a click. function exampleTwo() we will bind to a namespace of the click. function exampleThree() we will bind to a namepsace of the click, but unbind and bind multiple times without ever removing the other binds which prevents duplicating binding while not removing any other of the bound methods.

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

Example Start: (Create element to bind to and some methods to be our handlers)

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

<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!'); }

Bind exampleOne to click:

将exampleOne绑定到单击:

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

Now if user clicks the button or call $('#button').trigger('click') you will get the alert "One Fired!";

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

Bind exampleTwo to a namespace of click: "name is arbitrary, we will use myNamespace2"

将exampleTwo绑定到click的命名空间:“name是任意的,我们将使用myNamespace2”

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

The cool thing about this is, we can trigger the "click" which will fire exampleOne() AND exampleTwo(), or we can trigger "click.myNamespace2" which will only fire exampleTwo()

关于这个很酷的事情是,我们可以触发“click”,这将触发exampleOne()和exampleTwo(),或者我们可以触发“click.myNamespace2”,它只会触发exampleTwo()

Bind exampleThree to a namespace of click: "again, name is arbitrary as long as it's different from exampleTwo's namespace, we will use myNamespace3"

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

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

Now if 'click' get's triggered ALL three example methods will get fired, or we can target a specific namespace.

现在,如果触发'click'get,则会触发所有三个示例方法,或者我们可以定位特定的命名空间。

PUT IT ALL TOGETHER TO PREVENT DUPLICATE

把它们放在一起以防止重复

If we were to continue to bind exampleThree() like so:

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

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

They would get fired three times because each time you call bind you add it to the event array. So, really simple. Just unbind for that namespace prior to binding, like so:

它们会被激活三次,因为每次调用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);

If the click function is triggered, exampleOne(), exampleTwo(), and exampleThree() only get fired once.

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

To wrap it all together in a simple function:

在一个简单的函数中将它们包装在一起:

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

Summary:

概要:

jQuery event namespaces allow for binding to main event but also allow child namespaces to be created and cleared without effecting sibling namespaces or parent ones which with very minimal creative thinking allows prevention of duplicate bindings.

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

For further explanation: http://api.jquery.com/event.namespace/

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

#2


22  

An excellent answer from

一个很好的答案

bind event only once

绑定事件只有一次

copyPastedInfo:

copyPastedInfo:

if you can apply it, probably want to take a look at event.preventDefaultt and event.stopPropagation OR unbind and bind each time, within the method like

如果你可以应用它,可能想看一下event.preventDefaultt和event.stopPropagation或者unbind和bind,每次都在这样的方法中

function someMethod()
{
  $(obj).off('click').on('click', function {});
}

#3


8  

Ummm how about using one()? http://api.jquery.com/one/

嗯,如何使用一个()? http://api.jquery.com/one/

Or am i completely misunderstanding you?

还是我完全误解了你?

#4


7  

This isn't a full answer, but I quickly found how to fix all my code with minimal changes. In my "base" js class (this is an object which is used on every page for standard button hookups by class name etc) I added the following method which does the check for dupe handlers:

这不是一个完整的答案,但我很快就找到了如何用最少的更改修复我的所有代码。在我的“基础”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);
    }
},

Then I just invoke it instead of the bind method!

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

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

Note that you can't use anon methods here as they would just be called 1, 2, 3 etc and the only way to check for dupes would be with a toString() on the method, which would be pretty slow in a complex application

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

#5


1  

About a hundred years too late, but if you're not using anonymous functions you can do this much more simply by using unbind first:

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

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

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

This implementation will work in Firefox 4, but not in many other browsers - because the handler variable is created new each time so the unbind can't find a matching handler to unbind. Moving the handler into the parent scope fixes the problem: i.e.

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

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

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

Presumably Firefox is doing some clever optimisation which means the handler is held as a constant variable somewhere as it never changes.

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

#1


49  

Prevent duplicate binding using jQuery's event namespace

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

There are actually a couple different ways of preventing duplicate. One is just passing the original handler in the unbind, BUT if it is a copy and not in the same space in memory it will not unbind, the other popular way (using namespaces) is a more certain way of achieving this.

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

This is a common issue with events. So I'll explain a little on the jQuery events and using namespace to prevent duplicate bindings.

这是事件的常见问题。所以我将解释一下jQuery事件并使用命名空间来防止重复绑定。



ANSWER: (Short and straight to the point)

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


// 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!



EXAMPLE OF ANSWER: (Full detailed explanation)

答案示例:(详细说明)


First let's create an example element to bind to. We will use a button with the id of #button. Then make 3 functions that can and will be used as the handlers to get bound to the event:

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

function exampleOne() we will bind with a click. function exampleTwo() we will bind to a namespace of the click. function exampleThree() we will bind to a namepsace of the click, but unbind and bind multiple times without ever removing the other binds which prevents duplicating binding while not removing any other of the bound methods.

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

Example Start: (Create element to bind to and some methods to be our handlers)

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

<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!'); }

Bind exampleOne to click:

将exampleOne绑定到单击:

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

Now if user clicks the button or call $('#button').trigger('click') you will get the alert "One Fired!";

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

Bind exampleTwo to a namespace of click: "name is arbitrary, we will use myNamespace2"

将exampleTwo绑定到click的命名空间:“name是任意的,我们将使用myNamespace2”

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

The cool thing about this is, we can trigger the "click" which will fire exampleOne() AND exampleTwo(), or we can trigger "click.myNamespace2" which will only fire exampleTwo()

关于这个很酷的事情是,我们可以触发“click”,这将触发exampleOne()和exampleTwo(),或者我们可以触发“click.myNamespace2”,它只会触发exampleTwo()

Bind exampleThree to a namespace of click: "again, name is arbitrary as long as it's different from exampleTwo's namespace, we will use myNamespace3"

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

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

Now if 'click' get's triggered ALL three example methods will get fired, or we can target a specific namespace.

现在,如果触发'click'get,则会触发所有三个示例方法,或者我们可以定位特定的命名空间。

PUT IT ALL TOGETHER TO PREVENT DUPLICATE

把它们放在一起以防止重复

If we were to continue to bind exampleThree() like so:

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

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

They would get fired three times because each time you call bind you add it to the event array. So, really simple. Just unbind for that namespace prior to binding, like so:

它们会被激活三次,因为每次调用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);

If the click function is triggered, exampleOne(), exampleTwo(), and exampleThree() only get fired once.

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

To wrap it all together in a simple function:

在一个简单的函数中将它们包装在一起:

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

Summary:

概要:

jQuery event namespaces allow for binding to main event but also allow child namespaces to be created and cleared without effecting sibling namespaces or parent ones which with very minimal creative thinking allows prevention of duplicate bindings.

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

For further explanation: http://api.jquery.com/event.namespace/

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

#2


22  

An excellent answer from

一个很好的答案

bind event only once

绑定事件只有一次

copyPastedInfo:

copyPastedInfo:

if you can apply it, probably want to take a look at event.preventDefaultt and event.stopPropagation OR unbind and bind each time, within the method like

如果你可以应用它,可能想看一下event.preventDefaultt和event.stopPropagation或者unbind和bind,每次都在这样的方法中

function someMethod()
{
  $(obj).off('click').on('click', function {});
}

#3


8  

Ummm how about using one()? http://api.jquery.com/one/

嗯,如何使用一个()? http://api.jquery.com/one/

Or am i completely misunderstanding you?

还是我完全误解了你?

#4


7  

This isn't a full answer, but I quickly found how to fix all my code with minimal changes. In my "base" js class (this is an object which is used on every page for standard button hookups by class name etc) I added the following method which does the check for dupe handlers:

这不是一个完整的答案,但我很快就找到了如何用最少的更改修复我的所有代码。在我的“基础”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);
    }
},

Then I just invoke it instead of the bind method!

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

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

Note that you can't use anon methods here as they would just be called 1, 2, 3 etc and the only way to check for dupes would be with a toString() on the method, which would be pretty slow in a complex application

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

#5


1  

About a hundred years too late, but if you're not using anonymous functions you can do this much more simply by using unbind first:

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

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

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

This implementation will work in Firefox 4, but not in many other browsers - because the handler variable is created new each time so the unbind can't find a matching handler to unbind. Moving the handler into the parent scope fixes the problem: i.e.

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

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

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

Presumably Firefox is doing some clever optimisation which means the handler is held as a constant variable somewhere as it never changes.

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