I want to bind an event to a jQuery element so it calls a function in my class, but when the function gets called, this variable no longer points to the class, instead pointing to the sender element.
this.remove = function() {
alert(this);
/* Shows [object HTMLImageElement] instead of the desired class */
this.dv.remove(); /* error */
}
$(this.buttons['cancel']).bind("click", this.remove);
How can I get around that?
$(this.buttons['cancel']).bind("click", $.proxy(this.remove, this));
Where the second argument is the context that you want the method to execute in.
Just put the correct this in a closure:
var that = this;
this.remove = function() {
alert(that);
that.dv.remove();
}
$(this.buttons['cancel']).bind("click", this.remove);
Use jQuery proxy to preserve the context.
this.remove = function() {
alert(this);
/* Shows [object HTMLImageElement] instead of the desired class */
this.dv.remove(); /* error */
}
//Now in the click handler this will point to the context which we pass in $.proxy
$(this.buttons['cancel']).bind("click", $.proxy(this.remove, this));
You need to call it inside a function, just using this.remove won't have the desired effect. And, you have to capture this in a different variable, because it will change inside the function:
var self = this;
$(this.buttons['cancel']).bind("click", function () { self.remove(); });
Related
I have an object. It initializes a button to alert "Hello!" when it is clicked. Why won't this work?
jsfiddle: http://jsfiddle.net/kUT52/1/
HTML
<button>Test</button>
JS
var MyObject = {
testValue: "Hello!",
testFunction: function() {
alert(this.testValue);
},
init: function(button) {
button.click(this.testFunction());
}
}
$(document).ready(function(){
var buttonInstance = new MyObject();
var button = $('button');
buttonInstance.init(button);
});
Whenever you put () behind a function reference, you are executing the function. You have to pass the function reference to .click, not what the function returns (unless the function returns a function that you want to use as event handler).
Example:
button.click(this.testFunction);
But now you have another problem: Inside the function, this will refer to the DOM element and not to the object, so accessing this.testValue will return undefined.
You can use jQuery's $.proxy function to fix this:
button.click($.proxy(this.testFunction, this));
Now this will refer to the object, and you can get a reference to the clicked element via event.target.
For two reasons:
You are using testFunction() instead of testFunction when you bind the event, so you will be calling the function and binding the (undefined) return value.
When you use a method as a function, it's no longer attached to the object, so the context will not be the object when the callback is called, but the global window object.
Use the proxy method to make a function that calls the method with the right context:
button.click($.proxy(this.testFunction, this));
That's an object literal, and you'd normally use it like so :
var MyObject = {
testValue: "Hello!",
testFunction: function() {
alert(MyObject.testValue);
},
init: function(button) {
button.on('click', this.testFunction);
}
}
var button = $('button');
MyObject.init(button);
FIDDLE
or passing the object:
var MyObject = {
testValue: "Hello!",
testFunction: function(e) {
alert(e.data.obj.testValue);
},
init: function(button) {
button.on('click', {obj: this}, this.testFunction);
}
}
var button = $('button');
MyObject.init(button);
FIDDLE
I've created some kind of JavaScript object with the such design pattern:
var MyObject = (function(){
var element;
var config = {
// Defaults...
};
function initialize(id, options){
element = $(id);
config = $.extend(config, options);
}
function method(){
// Some code...
}
element.on('click', function(){
// Some actions when clicked on element...
});
return {
initialize: initialize,
// Other public methods...
};
})();
And that's how object is initialized:
MyObject.initialize('#someId');
Script works great, however an error occurs, when I try to add some events for element.
As I realized, an anonymous function (function(){ ... })(); is executed immediatelly, but initialization of variable elementcomes after.
So, how can I implement an event handling exactly for this JavaScript object pattern?
Thanks!
You should be calling on inside the initialize function. Since the outer function is executing immediately (as you realized yourself), element is undefined. So you should only be calling on on it, after the variable has been defined:
function initialize(id, options){
element = $(id);
element.on('click', function() {
...
});
config = $.extend(config, options);
}
Stick it in the initialize method.
function initialize(id, options){
element = $(id);
config = $.extend(config, options);
element.on('click', function(){
// Some actions when clicked on element...
});
}
What I want to do is to execute the create_tag function when a specified condition is satisfied. I am referring to this function as a method of an object, in this case document.body, by setting as its method an external function, "create_tag(..)". The problem is inside this function I have a "this" keyword which I would expect to refer to the method's parent, document.body. Instead it doesn't seem to work. I tried replacing "this" with "document.body" in the function so the problem should be caused by "this".
Here is the code:
xmlDom=xmlhttp.responseXML;
hint_ul=document.getElementById("hint_ul");
personaggi=xmlDom.documentElement.getElementsByTagName("personaggio");
for(i=0;i<personaggi.length;i++){
personaggio=personaggi.item(i);
name=personaggio.childNodes[1].firstChild.nodeValue;
if(name.substr(0, str.length).toLowerCase()==str.toLowerCase()){
document.body.crea_li=create_tag(name);
}
}
}
function create_tag(inner){
a=document.createElement("a");
a.innerHTML=inner;
this.appendChild(a); }
this will be window when called like that.
To get its this as the body element, call it like so...
document.body.crea_li = create_tag.call(document.body, name);
Nowhere in your code is create_tag assigned as a method of document.body. The closest you get is with the line document.body.crea_li=create_tag(name);, but what's actually happening here is that you are executing create_tag as a member of the global object, and the result of that operation is assigned to document.body.crea_li.
You could make a reference to this outside the function body - referencing it within the scope later:
var self = this;
function create_tag(inner){
a=document.createElement("a");
a.innerHTML=inner;
self.appendChild(a);
}
This could be a nice trick. When I make complicated javascript objects involving many objects and functions, at the top of the object I create:
var self = this;
as that will live within the scope, the root object is always accessible.
Here is a working example of how I would implement this:
SomeReallyComplexThing = function() {
var self = this;
var foo = 'bar'
this.fooThing = 'Other thing'
this.setSomeData = function(){
console.log('Some data set', arguments)
}
this.makeMassiveCall = function() {
var completeFunc = function(){};
var url = '/some/endpoint.json';
var requestData = {};
jQuery.get(url, requestData, function(data) {
/*
* Data has come back
*/
self.setSomeData(data)
completeFunc(data);
});
}
}
//outside the scope
s = new SomeReallyComplexThing()
s.fooThing() //visible
s.self //undefined
this in javascript is a sqirrely fellow. The idea is this refers to the current function context.
This means that when your running code inside the function this refers to that function's context, which does not have an appendChild method.
Normally you use a closure to keep a reference to the calling context around, something like this
var _self = this;
var result = func();
function func()
{
// _self is the calling context, this is the current context
}
Or you could pass a reference to the calling context:
document.body.crea_li=create_tag(name,this);
function create_tag(inner, context) { context.body.appendChild(...) }
this is referring to the function's parent, but its parent is actually the window object, not the document object or document.body. this actually refers to wherever context the function is called from, and in my opinion you should avoid using it to call methods just for that reason because it can be difficult to see what this is actually referring to. For example, if you called a function using this from another function, it would refer to the context within that function.
This example might help show what's going on:
var hello = function() {
alert( this.message );
}
window.message = "hello!";
hello()
You could document.body directly in the code like you suggested before, or you could pass another parameter that tells the function where to append the created tag:
function create_tag(inner, elementToAddTag){
a=document.createElement("a");
a.innerHTML=inner;
elementToAddTagTo.appendChild(a);
}
I habe read here about defining method for a Javascript class Advantages of using prototype, vs defining methods straight in the constructor? and I choose prototype way. But I get an issue, for example:
function MyClass() {};
MyClass.prototype.Hide = function() {};
function MyClass() {
this.layout = $("<div>", {id: "layout1"}).text("My content");
this.button = $("<input />", {id: "button1"});
this.layout.append(this.button);
$("#button1").click(function() {
//How can I call hide
this.Hide()//Error
});
}
MyClass.prototype.Hide = function() {
this.layout.hide("slow");
}
How can I call the prototype function in the contructor? I have try the forward declaration for the prototype method, but I think the issue is the way I call it, this.Hide() is no help!
Thanks for your time!
You're using the wrong this. The this you're using to call Hide() is actually the #button element. Assign the this that is the MyClass object to a local variable, and then use that in the click delegate:
...
this.layout.append(this.button);
var $this = this;
$("#button1").click(function() {
$this.Hide();
});
...
$("#button1").click(function() {
//How can I call hide
this.Hide()//Error
});
In this line of code, this refers to the button (it's inside a function).
Before ths binding, you can define var that = this; and use thatin the callback:
function MyClass() {};
MyClass.prototype.Hide = function() {};
function MyClass() {
var that = this;
this.layout = $("<div>", {id: "layout1"}).text("My content");
this.button = $("<input />", {id: "button1"});
this.layout.append(this.button);
$("#button1").click(function() {
//How can I call hide
that.Hide();
});
}
MyClass.prototype.Hide = function() {
this.layout.hide("slow");
}
You're not calling Hide in the constructor. You're calling it in the click callback, which has a different context (this is different).
Use a temp variable to store a reference to the current object:
var t;
t = this;
...click(function () {
t.hide();
});
Also, JavaScript convention is that PascalCase is used for constructors, and camelCase is used for functions/methods.
You can call prototype methods from constructor. You problem is that you are loosing context inside anonymous click function. So you have two options:
// 1. link to original object
var self = this;
$("#button1").click(function() {
self.Hide();
});
// 2. use proxy (bind) to invoke method in correct context
// there is built in function available in jQuery
$("#button1").click($.proxy(function() {
this.Hide();
}, this));
i hope anybody can help me =)
I try to keep the example simple:
function myClass(){
//some Code
}
myClass.prototype.func1 = function(){
//some Code
}
myClass.prototype.func2 = function(){
document.getElementById("myEl").onclick = function(){
this.func1 //does not work, this is only the Element...
}
}
How does it works to call func1? I want to bind the onclick-event in func2.
First, you have to keep a reference to the current object. As you already noticed, inside the event handler, this refers to the DOM element. You can do this with
var self = this;
Second, you have to actually call the function:
self.func1();
Complete example:
myClass.prototype.func2 = function(){
var self = this;
document.getElementById("myEl").onclick = function(){
self.func1();
};
}
In the newer browsers you can also use .bind() [MDN] to explicitly define what this should refer to (see the MDN docs for a shim for other browsers):
document.getElementById("myEl").onclick = this.func1.bind(this);
Inside the onclick function, the this value is bound to the element being clicked. You need to keep a copy (a reference) to the this inside of func2:
myClass.prototype.func2 = function(){
var self = this; //keep a reference to the this value
document.getElementById("myEl").onclick = function(){
self.func1();
}
}