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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2180326/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:07:15  来源:igfitidea点击:

JQuery event model and preventing duplicate handlers

jqueryeventstriggersbindjavascript

提问by Mr AH

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 中。我面临的问题与事件有关。假设我们从父页面触发(“猴子”),并在加载的页面上绑定(“猴子”)并执行警报(“猴子绑定”)。如果多次调用同一个加载方法,则多次调用绑定。现在我可以在绑定之前解除绑定,或者在绑定之前检查处理程序的数量然后不绑定它以防止这种情况发生。如果我以后想在另一个“子页面”(加载到 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 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;
    }

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 处理的触发器)。

回答by eli geske

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.

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

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:

首先让我们创建一个要绑定到的示例元素。我们将使用一个带有#button id 的按钮。然后创建 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() 我们将通过单击进行绑定。函数 exampleTwo() 我们将绑定到点击的命名空间。函数 exampleThree() 我们将绑定到点击的命名空间,但多次取消绑定和绑定而不删除其他绑定,这可以防止重复绑定同时不删除任​​何其他绑定方法。

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').trigger('click') 您将收到警报“One Fired!”;

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

将 exampleTwo绑定到 click 的命名空间:“名称是任意的,我们将使用 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() AND 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:

它们会被触发 3 次,因为每次调用 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.

如果点击函数被触发,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/

回答by Mr AH

An excellent answer from

来自的优秀回答

bind event only once

只绑定一次事件

copyPastedInfo:

复制粘贴信息

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

如果你可以应用它,可能想看看event.preventDefaulttevent.stopPropagation或者每次都取消绑定和绑定,在方法中

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

回答by prodigitalson

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

嗯嗯如何使用one()http://api.jquery.com/one/

Or am i completely misunderstanding you?

还是我完全误解了你?

回答by Mr AH

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

请注意,您不能在此处使用匿名方法,因为它们只会被称为 1、2、3 等,并且检查欺骗的唯一方法是在方法上使用 toString(),这在复杂的应用程序中会非常慢

回答by Massif

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 中工作,但不适用于许多其他浏览器 - 因为每次都会创建新的处理程序变量,因此解绑无法找到匹配的解绑处理程序。将处理程序移动到父作用域可以解决问题:即

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 正在做一些巧妙的优化,这意味着处理程序在某处作为常量变量保存,因为它永远不会改变。