1

I have a basic javascript class with two properties. I want to log the value of one on a click event. Here is what I got:

function Clicker(/*string*/var1, /*id*/var2) {
    this.name = var1;
    this.clickerid = var2;
    this.clickevent = function() {
        console.log("1: " + this.name);
        console.log("2: " + this);
        console.log("3: " + window.testClicker.name);
    };

    var element = document.getElementById(this.clickerid);
    element.addEventListener("click", this.clickevent, false);
}

window.onload = function() {
    window.testClicker = new Clicker("lorem ipsum", "checkbox1");
};

<input id="checkbox1" type="checkbox" value="1" checked>

When I run my test I see the following in the log:

1: undefined
2: <input id=​"checkbox1" type=​"checkbox" value=​"1" checked>​
3: lorem ipsum

I was expecting to see the first and third lines match. Suggestions?

TheRed__
  • 164
  • 11

3 Answers3

3

The value of this depends on the context in which a function is called.

When the event listener fires, the function is called in the context of the element the event is associated with - not the object the function was copied from.

Use bind to override the context (it generates a new function that calls the original function in whatever context you define).

element.addEventListener("click", this.clickevent.bind(this), false);

This is equivalent to:

element.addEventListener(
    "click", 
    function (context) {
        return function () {
            context.clickevent();
        };
    }(this),
    false
);
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
2

I think it might be a context issue, could the following work:

function Clicker(/*string*/var1, /*id*/var2) {
    this.name = var1;
    this.clickerid = var2;
    var self = this;
    this.clickevent = function() {
        console.log("1: " + self.name);
        console.log("2: " + this);
        console.log("3: " + window.testClicker.name);
    };

    var element = document.getElementById(this.clickerid);
    element.addEventListener("click", this.clickevent, false);
}

as you can see with the 2nd console.log, this refers to the checkbox, not the Cliker object

dm03514
  • 54,664
  • 18
  • 108
  • 145
  • 1
    It isn't scope, it is context. – Quentin Sep 16 '13 at 20:46
  • @Quentin I've seen controversies about calling it "context" too (although several libraries do that). Some say it might be confused with "execution context". I personally have been using "`this` value", which is very close to what the spec calls it. – bfavaretto Sep 16 '13 at 20:51
  • This approach worked. Thanks. But I'm sticking with the approach shown above by Quentin since there is one less variable to define. – TheRed__ Sep 16 '13 at 21:01
  • Actually, after playing with this for a bit. I like this approach as equally as much. When I don't bind, I have a useful way to operate on the checkbox element itself AND access to the class object through self. – TheRed__ Sep 16 '13 at 22:15
0

For the sake of completeness, there are three possible solutions:

  1. Using a closured variable as suggested in dm03514's answer.
  2. Using bind as suggested in Quentin's answer.
  3. Using the event listener interface / handleEvent as suggested at Dealing with Scope in Object methods containing 'this' keyword called by Event Listeners .
Community
  • 1
  • 1
bfavaretto
  • 71,580
  • 16
  • 111
  • 150