According to John Resig's 'Learning Advanced JavaScript' () 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?
According to John Resig's 'Learning Advanced JavaScript' (http://ejohn/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?
Share Improve this question asked Dec 14, 2013 at 17:23 Ivan VashchenkoIvan Vashchenko 1,2242 gold badges11 silver badges20 bronze badges 3-
What do you mean? That code will set the "clicked" property of the
<li>
. – Pointy 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. – Pointy Commented Dec 14, 2013 at 17:35
- it's not "wrong" if it works, it's just potentially confusing to other coders... – dandavis Commented Dec 14, 2013 at 18:09
4 Answers
Reset to default 9In 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
You can also use Function.prototype.bind
to get around this
elem.onclick = Button.click.bind(Button);
Note .bind
requires ECMAScript >= 5
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.
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(); };