3

For a particular listener in my application, I'm using the following code for scope-busting purposes:

// this is all in a prototype of MyClass
var self = this;
myElement.addEventListener("stuff", function(e){self.doStuff(e)});

That will get doStuff to have the desired this binding.

The problem shows up when I try to removeEventListener. I suppose it's because the native function signatures must be different?

// in a different prototype of MyClass
var self = this;
myElement.removeEventListener("stuff", function(e){self.doStuff(e)}); // doesn't work

If I make a separate function that contains all of my scope-busting code, then the this binding in that code will be to the unwanted object of myElement. So the question is: How can I force listener scope and still be able to remove an added event listener?

*note using global / static variables in any way is prohibited due to the nature of the project (otherwise this would be simple!)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284

3 Answers3

4

This has nothing to do with scope or the way in which you're storing a reference to this. The problem is that removeEventListener expects a reference to a function that's previously been registered as a listener, but you're giving it a brand new function it's never seen before.

You need to do something like this:

var self = this;
var listener = function(e){self.doStuff(e)}
myElement.addEventListener("stuff", listener);
// later
myElement.removeEventListener("stuff", listener);

It doesn't matter that the bodies of your two functions are the same; they're still different functions.

See:

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • You touch on a very simple principle of variables that stay in scope. Thanks for the reminder. I had to make your listener variable a property/method for the class, but then used that to work the magic. Thanks. – Jacksonkr Jan 06 '12 at 19:40
1

Inline anonymous functions are a very bad practice anyway, so I will suggest the obvious:

function MyClass() {
    this.onStuff = this.onStuff.bind(this); //Each instance steals the prototyped function and adds a bound version as their ownProperty
}

MyClass.prototype = {

    onStuff: function (e) { //Prototyped, no instance actually uses this very function
        this.dostuff()
    },

    bind: function () {
        myElement.addEventListener("stuff", this.onStuff);
    },

    unbind: function () {
        myElement.removeEventListener("stuff", this.onStuff);
    }

}
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • *"Inline anonymous functions are a very bad practice anyway..."* Says who? – Wayne Jan 06 '12 at 19:39
  • 3
    @lwburk says me. Do you need an authority to say it or it's not obvious enough from seeing code that has 15 levels of redundant indentation caused by the practice? – Esailija Jan 06 '12 at 19:48
  • @lwburk creating new functions when you can re-use other functions is a bad practice. It creates a memory overhead where it's not needed. There are other older issues like inline anonymous functions can cause memory leaks in legacy browsers (due to DOM references) and unnamed inline anonymous functions hurt readability and nested functions hurt readability, etc, etc. – Raynos Jan 06 '12 at 19:50
  • @Raynos - Sure, but it's usually not necessary to name a function -- a one-off callback, for example -- that won't be used anywhere else. And writing it anonymously indicates immediately that this is the only place it will ever be used. – Wayne Jan 06 '12 at 19:51
  • @Esailija - +1 for "says me". – Wayne Jan 06 '12 at 19:52
  • @lwburk every callback you forgot to name will haunt you when you see it in a stack trace and it becomes a pain in the ass to debug. – Raynos Jan 06 '12 at 19:52
0

see removeEventListener on anonymous functions in JavaScript

You can't removeEventListener as your using an anonymous function.

Community
  • 1
  • 1