3

According to John Resig's 'Learning Advanced JavaScript' (http://ejohn.org/apps/learn/#83) it's incorrect to bind an object's method to event handler without passing the original object as context, however I find the example flawed. It claims the clicked property gets set accidentally. Here's a counter-example.

var Button = {
  click: function(){
    this.clicked = true;
    console.log( elem.clicked );
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click;
document.children[0].appendChild(elem);

console.log( !elem.clicked );

There must be another reason not to do this. What is it?

3
  • What do you mean? That code will set the "clicked" property of the <li>. Commented Dec 14, 2013 at 17:32
  • Also you skipped the very important line of code in that example, where the "click" event was actually triggered. In your code above, the "click" function will never even be called. Commented Dec 14, 2013 at 17:35
  • it's not "wrong" if it works, it's just potentially confusing to other coders... Commented Dec 14, 2013 at 18:09

4 Answers 4

9

In any object method, this always refers to the object the method has been called on. It's true for all JavaScript that this provides the calling context, not the method owner (*).

var sample = {
    foo: function () {
        this.clicked = true;
    }
}

sample.foo();         // 'this' refers to 'sample' 
alert(sample.clicked) // true

In an event handler this refers to the element that triggered the event. This means when you pass an object method to the click event...

var div = document.getElementById("test");
div.onclick = sample.foo;

then foo() will be called on the DOM element, even though it has been defined elsewhere.

/* ... click the div ... */ 

alert(sample.clicked); // false
alert(div.clicked);    // true

which will lead to unexpected results.


(*) That's because technically there is no method owner in JavaScript. Methods are standalone functions that happen to be referenced by object properties. Storing a reference to the same function in a different object (div.onclick = sample.foo does just that) is easily possible.

Consequently, obj.method() is syntactic sugar.

function func() { /* ... */ }
var obj = {
    method: func
};

obj.method();
func.call(obj);  // same thing
Sign up to request clarification or add additional context in comments.

Comments

3

You can also use Function.prototype.bind to get around this

elem.onclick = Button.click.bind(Button);

Note .bind requires ECMAScript >= 5

Comments

2

What Mr. Resig is talking about is the fact that in JavaScript, this gets a value in a way that's distinctly different from how this works in some other languages. In this example, I think his point is that the reference to this in the "click" function on that "Button" object doesn't necessarily refer to that object. Instead, its value is determined only by the situation in which the function is called.

Thus, when you use that function as an event handler, the value of this in the function will be a reference to the element that was clicked.

Comments

0

Here's a simple example, without events.

var foo = {
  method: function() {
    this.prop = true;
  }
};

var bar = {};

bar.method = foo.method;
bar.method();

console.log( foo.prop ); // => undefined
console.log( bar.prop ); // => true

foo.method();

console.log( foo.prop ); // => true

It seems like in your example, you're checking the state of elem.clicked before clicking the element. Click it, and the property will get set (but Button.clicked won't be set!). The correct way to do this is simply:

elem.onclick = function() { Button.click(); };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.