0

EDIT : I need to invoke one function which fetches data and reloads the page's contents. But this has to be invoked once another function has fetched data(webSql). I cannot use the WebSql callback as variables are out of scope. So I created a custom Event and added a listener in the second function scope. So when data is fetched I am dispatching the event in the first function scope. Now the issue if the page was reloaded more than once, listeners will get added multiple times and all will be invoked which I dont want.

I need to make sure that only one function is listening to a custom event. Right now am removing the listener once its invoked like this :

document.addEventListener("customEvent", function () {
  actualCallBack(var1, var2); // Since I need to pass parameters I need to use callBack within an anonymous function.
  this.removeEventListener("customEvent", arguments.callee, false);
}, false);

But the problem is anonymous function will be removed only after its invoked in the first place. There is a possibility of listener getting added mulitple times. How can I remove event listeners before adding a new one ?

document.removeEventListener("customEvent");
document.addEventListener(...);

I could have removed it, if a variable function was used instead, but I need to pass some parameters to callback so I need to use anonymous functions.

GoodSp33d
  • 6,252
  • 4
  • 35
  • 67
  • IIRC, `cloneNode` clones everything but event listeners. Doing that in `document`-scope may be hilarious, though.. – fjdumont Jun 30 '13 at 13:20
  • 1
    One way would be to put the whole event binding in a custom function. That function can then keep a reference to the passed handler and make sure that only one handler is bound. Of course that requires that the handler is only bound to the element via that function. – Felix Kling Jun 30 '13 at 13:20
  • 1
    I don't understand your explanation why you have to use an anonymous function. Here is a related question btw: http://stackoverflow.com/questions/4386300/javascript-dom-how-to-remove-all-events-of-a-dom-object – basilikum Jun 30 '13 at 13:27
  • @FelixKling Am not binding events to div elements. Am invoking event listeners using `dispatchEvent` in another function. So I need to remove anything which was added before. – GoodSp33d Jun 30 '13 at 13:46
  • @basilikum I have edited question. I need to pass some parameters so I am wrapping the callBack within an anonymous function. – GoodSp33d Jun 30 '13 at 14:19
  • @2-Stroker, could you explain your use case a bit more? We could figure out a better solution than a generic "remove anonymous listeners" thing. – Dogbert Jun 30 '13 at 14:25
  • @Dogbert I have edited the question, added more detail. – GoodSp33d Jun 30 '13 at 14:31
  • @2-Stroker, check out my solution. – Dogbert Jun 30 '13 at 16:30

3 Answers3

3

using felix's suggestion

var setSingletonEventListener = (function(element){
    var handlers = {};
    return function(evtName, func){
        handlers.hasOwnProperty(evtName) && element.removeEventListener(evtName, handlers[evtName]);
        if (func) {
            handlers[evtName] = func;
            element.addEventListener(evtName, func);
        } else {
            delete handlers[evtName];
        }
    };
})(document);

setSingletonEventListener("custom event", function(){

});

//replaces the previous
setSingletonEventListener("custom event", function(){

});

//removes the listener, if any
setSingletonEventListener("custom event");
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
goat
  • 31,486
  • 7
  • 73
  • 96
1

Here's one way:

var singletonEventListenerFor = function (element, eventName) {
    var callback = null;
    element.addEventListener(eventName, function () {
        callback && callback();
    });
    return function (set) {
        callback = set;
    };
};

Testing:

var event = document.createEvent("Event");
event.initEvent("customEvent", true, true);

var listener = singletonEventListenerFor(document, "customEvent");

var counter = 0;

listener(function () {
    console.log(++counter);
});

// counter === 1
document.dispatchEvent(event);

// Remove the listener
listener();
// This shouldn't increment counter.
document.dispatchEvent(event);

listener(function () {
    console.log(++counter);
});

// counter === 2
document.dispatchEvent(event);
// counter === 3
document.dispatchEvent(event);

console.log('3 === ' + counter);

http://jsfiddle.net/Dogbert/2zUZT/

API could be improved by returning an object with .set(callback) and .remove() functions instead of using a single function to do both things, if you like.

Dogbert
  • 212,659
  • 41
  • 396
  • 397
0

Store somewhere that you've already applied the listener, and only add it, if it hasn't been added already.

DanMan
  • 11,323
  • 4
  • 40
  • 61