jQuery use an object's method as an event handler - javascript

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

Related

JS - called method is not a function

I have object:
var devark = {
init: function() {
var obj = this;
obj.assignHandlers();
},
assignHandlers: function() {
var obj = this;
document.getElementById("menu-toggler").onclick = obj.documentFunctions[0];
},
documentFunctions: [
function() {
toggleClass(this, "opened");
}
]
};
on window.load, I am calling the init method. That works fine but when it calls another object method assignHandlers, it throws an error:
[17:54:33.192] TypeError: obj.assignHandlers is not a function
Why is it?
Like #Bergi said, it's a this value issue that can be solved by doing:
window.onload = function () {
devark.init();
};
The difference between both ways is the value of this within the init function. To determine the natural value of this, look at the left-side of the . in a method call, such as obj.someFn();. Here the value of this within someFn will be obj. However, when you do window.onload = devark.init;, you can imagine the handler will later be invoke like window.onload(). Which means the value of this within the onload function, which is really the init function will be window, not devark.
You can also use Function.prototype.bind to bind a specific this value to a function.
window.onload = devark.init.bind(devark);

Call this.instanceMethod inside another instance method event handler

I am trying to call instance method inside event handler of another instance method but I am getting function undefined, I assume this is because in the event handler "this" refers to DOM element rather then the instance:
function MyObject(something) {
this.something = something;
this.value = 'abc';
}
MyObject.prototype.Init = function() {
$(this.something).click(function() {
this.DoSomething();
});
};
MyObject.prototype.DoSomething = function() {
//do something
};
Is there way to get "this" to point to instance?
Define another variable whose name is something other than this and assign it the context you wish to refer to in your inner function:
MyObject.prototype.Init = function() {
var scope = this;
$(this.something).click(function() {
scope.DoSomething();
});
};

Access dialog in callback method with proxy

I am currently creating a dialog within a user-define class:
$("<div>").dialog(buttons: {
'one': function () {
$(this).dialog('close').dialog('destroy');
}
});
The above works fine, however, this no longer refers to the class instance in the above function. I can get around this with $.proxy:
...buttons: {
'one': $.proxy(function () {
this.doWork();
}, this)
Then, I can call class methods when the dialog button is clicked.
However, I still need to call .dialog('close').dialog('destroy') on the dialog element itself. After redefining this with $.proxy, how can I access that element in the button callback? e.target refers to the button itself.
I also realize I can do something like this:
var obj = this;
...buttons: {
obj.doWork();
but I'm looking for a way around that.
I'm not sure why you want to avoid var obj = this; inside the class's scope, but the only other way would be with a self-invoking closure which does essentially the same thing. In order to have a reference to both contexts, you need to store the class reference in a different variable.
With closure:
function MyClass() {
this.createDialog = function () {
$("<div>").dialog({
buttons: {
"one": function (self) {
return function (e) {
self.doWork();
$(this).dialog("close").dialog("destroy");
};
}(this)
}
});
};
this.doWork = function () {
// do work
};
}
$(function () {
var obj = new MyClass();
$(".createDialog").click(function () {
obj.createDialog();
});
});
jsFiddle: http://jsfiddle.net/ar4ZL/

How to use prototype method in class constructor

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));

Preserve 'this' with jQuery.bind?

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(); });

Categories