Javascript 事件 addEventListener 为同一功能多次注册;使用 OOP Javascript

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26127336/
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-10-28 05:32:25  来源:igfitidea点击:

Javascript event addEventListener registering multiple times for same function; using OOP Javascript

javascriptoopeventsjavascript-eventsaddeventlistener

提问by user717236

I'm employing Object-Oriented Javascript, in conjunction with registering event listeners. From what I understand about event listeners, if the function applied to eventtarget has already been registered, repeated attempts to add this same event listener will be ignored. In other words, it should only fire once. But that is not the case in the code below (also can be seen on jsfiddle).

我正在使用面向对象的 Javascript,并结合注册事件侦听器。根据我对事件侦听器的了解,如果应用于 eventtarget 的函数已经注册,则重复尝试添加相同的事件侦听器将被忽略。换句话说,它应该只触发一次。但在下面的代码中情况并非如此(也可以在 jsfiddle 上看到)。

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener

Multiple identical event listeners

If multiple identical EventListeners are registered on the same EventTarget with the same parameters, the duplicate instances are discarded. They do not cause the EventListener to be called twice, and since the duplicates are discarded, they do not need to be removed manually with the removeEventListener method.

多个相同的事件监听器

如果在同一个 EventTarget 上使用相同的参数注册了多个相同的 EventListeners,则丢弃重复的实例。它们不会导致 EventListener 被调用两次,并且由于重复项被丢弃,因此不需要使用 removeEventListener 方法手动删除它们。

http://jsfiddle.net/qd1e8f6c/

http://jsfiddle.net/qd1e8f6c/

HTML

HTML

<div id="wrapper">
    <input id="t1" type="text" />
    <input id="btn" type="button" />
</div>

JS

JS

var namespace = namespace || {};

namespace.event = {
    addListener: function(el, type) {
        var handle = function() {
            switch (type) {
                case "focus": 
                    console.log(el.value);
                    break;
                case "click":
                    console.log(el.id + " was clicked");
                    break;
            }
        };

        el.addEventListener(type, handle, false);
    }
};

namespace.ExampleClass = function() {
    this.init = function(el1, el2) {
        el1.value = "123";
        el2.value = "Click Me";
    };
};

var textbox = document.getElementById("t1");
var button = document.getElementById("btn");

var inst = new namespace.ExampleClass();

inst.init( textbox, button );

namespace.event.addListener(textbox, "focus");
namespace.event.addListener(button, "click");

// same handle -- shoudln't it only add the event once?
namespace.event.addListener(textbox, "focus");
namespace.event.addListener(button, "click");

As you can see in the last few lines of the code above, a function called addListeneris executed twice, which registers an event to each input. Then, addListeneris executed again. I'm expecting it to not register again and ignore, but it actually registers. I don't get it. The function in the namespace called handleis exactly the same. What am I doing wrong here?

正如您在上面代码的最后几行中看到的那样,一个被调用的函数addListener被执行了两次,它为每个输入注册了一个事件。然后,addListener再次执行。我希望它不会再次注册并忽略,但它实际上会注册。我不明白。调用的命名空间中的函数handle完全一样。我在这里做错了什么?

Any help would be great. Thank you so much.

任何帮助都会很棒。太感谢了。

回答by loganfsmyth

You cannot bind the same type/function pair to an element. However, that is not what you are doing, you are explicitly creating a new handlerfunction on every call to your namespace.addEventListenerfunction.

您不能将相同的类型/函数对绑定到一个元素。然而,这不是你在做什么,你handler在每次调用你的namespace.addEventListener函数时明确地创建一个新函数。

What you have:

你有什么:

namespace.event = {
    addListener: function(el, type) {
        var handle = function() {
            switch (type) {
                case "focus": 
                    console.log(el.value);
                    break;
                case "click":
                    console.log(el.id + " was clicked");
                    break;
            }
        };

        el.addEventListener(type, handle, false);
    }
};

What would do what you expect:

什么会做你期望的:

var handle = function(evt) {
    var el = evt.currentTarget;

    switch (type) {
        case "focus": 
            console.log(el.value);
            break;
        case "click":
            console.log(el.id + " was clicked");
            break;
    }
};

namespace.event = {
    addListener: function(el, type) {
        el.addEventListener(type, handle, false);
    }
};

because there is only one instance of handlein the second case.

因为handle在第二种情况下只有一个实例。

Namespacing

命名空间

What you have is one approach to namespacing, but most often these days, JS namespacing is done via the Module Pattern

您拥有的是一种命名空间的方法,但现在大多数情况下,JS 命名空间是通过模块模式完成的

For your case for example, you don't even appear to really care about making your code globally accessible via this 'namespace' variable since it is only used in your code, so you could do:

例如,对于您的情况,您似乎并不真正关心通过此“命名空间”变量使您的代码可全局访问,因为它仅在您的代码中使用,因此您可以这样做:

var namespace = (function(){
    function handle(evt) {
        var el = evt.currentTarget;

        switch (type) {
            case "focus": 
                console.log(el.value);
                break;
            case "click":
                console.log(el.id + " was clicked");
               break;
        }
    };

    function addListener(el, type) {
        el.addEventListener(type, handle, false);
    }

    function ExampleClass() {
        this.init = function(el1, el2) {
            el1.value = "123";
            el2.value = "Click Me";
        };
    };

    var textbox = document.getElementById("t1");
    var button = document.getElementById("btn");

    var inst = new ExampleClass();

    inst.init( textbox, button );

    addListener(textbox, "focus");
    addListener(button, "click");


    // And if you do care about 'inst' being global, you'd explicitly add it to the window.
    window.inst = inst;

    // Whatever functions you want to expose as 'namespace' would go here.
    return {
        event: {
            addEventListener: addEventListener
        }
    };
})();