Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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).

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.

http://jsfiddle.net/qd1e8f6c/

HTML

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

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 addListener is executed twice, which registers an event to each input. Then, addListener is 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 handle is exactly the same. What am I doing wrong here?

Any help would be great.

like image 470
user717236 Avatar asked Sep 30 '14 18:09

user717236


People also ask

Why is event listener firing multiple times?

This symptom is indicative that you've registered the same listener more than once. You must remember to deregister events when your component unloads to prevent his problem.

Can you add the same event listener twice?

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 they are discarded they do not need to be removed with the removeEventListener method.

How do you add an event listener to multiple elements with the same ID?

Adding event listener to multiple elements To add the event listener to the multiple elements, first we need to access the multiple elements with the same class name or id using document. querySelectorAll() method then we need to loop through each element using the forEach() method and add an event listener to it.

How do I add multiple events to an event listener?

So You need to write a custom function to iterate over multiple events. This is being handled in jQuery by using . split(" ") and then iterating over the list to set the eventListeners for each types .


1 Answers

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 handler function on every call to your namespace.addEventListener function.

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 handle in the second case.

Namespacing

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

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
        }
    };
})();
like image 125
loganfsmyth Avatar answered Oct 04 '22 02:10

loganfsmyth