I'm trying to create some select tags from within a closure function, and attach an event to them which calls another function within the closure.
Here's a very simplified version of my code:
var SomeClosure = function() {
this.build = function(){
var mydiv = document.getElementById('mydiv');
var newSelect = document.createElement('select');
newSelect.onchange = (function() {
var selfRef = this;
return function() {
selfRef.changeselection();
}
})();
mydiv.appendChild(newSelect);
};
this.changeselection = function(){
// do something
}
}
All I get, however, is 'selfRef.changeselection is not a function'. Where am I going wrong?
I don't need to pass in the value of the select, just call the function. If, however, I did need to pass in its value too, how would I do that?
Change your code to this:
var selfRef = this;
newSelect.onchange = (function() {
return function() {
selfRef.changeselection();
}
})();
The context inside immediate function is Window, so you get wrong reference. Also in this case you probably don't need IIFE at all, if this is all your code for onchange event handler.
Related
To tell the true, i can call the function but just in hard-coded way. Instead of hard-coding the submit binding my getData function, i'd like to call the function by arguments. Please help me, how to do this.
Thanks.
formhandler = new xForm.Main(); // new formhandler
formhandler.setForm(this.formid); // fn.setForm(string) // configure the container, which has the form elements, or the form itself
modal = new xModal.Main(); // new modal handler
modal.setModal(this.modalid); // set modal element
modal.showModal(); // show
modal.bindClose(".cancel"); // bind closing classes
modal.bindSubmit(".submit", formhandler, "getData"); // bind submit to call formhandler.getData()
in the xModal.js
var xModal = xModal || {};
xModal.Main = function()
{
var self = this;
...
this.bindSubmit = function(classname, a, b)
{
this.closeclass = classname;
$("#"+this.target).find(classname).click(function(){
a.call(b); // edited after the original post, i forgot to replace the call's argument to b in the question excuse me for the inaccuracy
});
}
this function should call the getData in the xForm (here is the snippet from xForm)
var xForm = xForm || {};
xForm.Main = function()
{
var self = this;
this.target = "";
this.data = {};
...
this.getData = function()
{
getSingleElements();
getMultiElements();
return returnData();
}
Update:
I think i just found a method to do this, but please tell me if i made something uncorrectly, or you have a better solution for this problem (i'm pretty sure someone has)
I think, i have the correct method.
in the xForm i made a fn, which calls functions by parameters contains in the self (which is equals to this, actually)
var xForm = xForm || {};
xForm.Main = function()
{
var self = this;
this.callFn = function(func)
{
return self[func].call(this);
}
...
then i call the fn from the another class (xModal)
var xModal = xModal || {};
xModal.Main = function()
{
var self = this;
this.bindSubmit = function(classname, a, b)
{
this.closeclass = classname;
$("#"+this.target).find(classname).click(function(){
a.callFn(b);
});
}
then i just have to tell the xModal this:
modal.bindSubmit(".submit", formhandler, "getData"); // bind submit to call formhandler.getData()
so now the modal class will call the args[1]'s args[2] function. also able to give more parameters to the call fn by apply method.
works fine at me, but i don't know, maybe you can help me in make this better.
You bind a method name of a certain object to the submit event:
modal.bindSubmit(".submit", formhandler, "getData");
But you want to pass arguments to the method as well. This is not the Javascript way of doing it. Instead, just bind an anonymous function to the event, and call the method however you like from within this anonymous function:
modal.bindSubmit(".submit", function(){
formhandler.getData("My arguments");
});
What you see in my example is an anonymous function passed as an argument. In Javascript, there is no distinction between a value like a string or an integer, and a function. A function can be assigned to a variable, and passed as an argument.
To make it more clear, you can also write it like this:
var eventHandler = function(){
formhandler.getData("My arguments");
};
modal.bindSubmit(".submit", eventHandler);
This is called "first class functions", and are part of the "functional programming" paradigm.
Inside the event handler function, you still have access to the variables in the scooe it was created in, like the formhandler object. This is called a "closure".
Read up on this. It will boggle your mind at first, but it is really worth your time, as it will open your eyes to much simpler solutions.
From your example, I am not sure what the object modal is. If it is a jQuery element, my example should work right away, othewise, you would need to update your code to call the function passed in as the event handler, instead of calling a method on an object.
function test() {
this.str = "hello";
this.sayHello = function() {
document.write(this.str);
}
this.init = function() {
document.onkeydown = this.sayHello;
}
}
var testing = new test();
testing.init();
The above code should output "hello" on an onkeydown event.
But I get "undefined". How can I get this to work ?
The problem is with this.sayHello. When you assign the reference to the sayHello function on keydown, the reference to the context (object) is lost. When a key is pressed, this refers to the Document object as the callback is invoked as:
document.onkeydown(); // or for simplicity imagine - document.sayHello();
If you assigned the str variable on the document object, you would see the value logged,
document.str = "hello";
However, that is not what you'd want. You need to wrap the keydown event handler inside another function to preserve the context to that object. Two ways to go about this. You could either wrap the event handler inside another function, and preserve the reference to this.
this.init = function() {
var me = this;
document.onkeydown = function() {
me.sayHello();
};
}
Or, if you're using modern browsers, this has already been incorporated into ECMAScript 5 using the bind function.
this.init = function() {
var me = this;
document.onkeydown = this.sayHello.bind(this);
}
I'm just curious, why is this event being loaded instead of triggering itself on click .
window.onload=initAll;
function initAll(){
var divPath = document.getElementsByTagName("div")[0];
var theLink = divPath.getElementsByTagName("a")[0];
theLink.onclick = myEvent(theLink);
};
function myEvent (myMan){
myMan.innerHTML="You're my maan,bro!!!";
return false;
};
10x for your kind help
BR
When you write theLink.onclick = myEvent(theLink), you're calling myEvent and assigning the result to onclick.
You need to create a separate function that calls myEvent with a parameter, and assign that to onclick:
theLink.onclick = function() { return myEvent(theLink); };
Because you are assigning the result of the function call myEventHandler(theLink) to the theLink.onclick property. What you are actually trying to do is the following:
theLink.onclick = myEventHandler
Which assigns a reference to the myEventHandler function to the theLink.onclick property. The argument passed to that function will be an event object, from which you can determine which object was actually clicked. Alternatively, you can create a closure:
theLink.onclick = function(event) {
myEventHandler(event, theLink);
};
This way you get the event object and a reference to the object which you assigned the event handler to, which is what (I guess that) you were trying to do in your code.
Its because as per your code you are assigning the value returned by the function myEvent as the theLink eventhandler instead of the function itself.
You should change the code to as follows:
window.onload=initAll;
function initAll(){
var divPath = document.getElementsByTagName("div")[0];
var theLink = divPath.getElementsByTagName("a")[0];
theLink.onclick = function(){ return myEventHandler(theLink)};
};
function myEvent (myMan){
myMan.innerHTML="You're my maan,bro!!!";
return false;
};
var A = function(x){
var that = this;
this.a = x;
}
A.prototype = {
init: function(){
alert(this.a); // as that is not found :s
}
};
var a = new A(1);
var b = new A(2)
a.init.call(b);
// I want to alert number 1, how to do it?
I need this because I use jQuery events.
my sick solution... not so good
I got my question answered but this has some problems, I have to define a local that var and create a closure for every event... sick!
var that = this;
this.view.skinAppliedSignal.add(function(){
that.onSkinApplied();
});
// then in the onSkinApplied the this is correct. any way not so hacky to get this done?
You cannot do that, in general. The only this that exists when a function runs is the this established by the invocation.
You can "trap" things in a closure when you establish event handlers:
function something() {
$('.whatever').each(function() {
var whatever = $(this);
whatever.find('button').click(function() {
whatever.hide();
});
});
}
In that example, "whatever" is used to save the element from the "each" loop so that the event handlers hooked up to button elements can get access to it.
edit — Based on a comment and the update to the question, it's clear that perhaps what's desired is something like the ".bind()" function that's part of ES5 (and which is supplied by some libraries, like Prototype, Functional, and jQuery). By using "bind", you basically wrap any function of your choice up in another function, such that your function will always be called with this set to some specific object.
Can't be done with a prototype. You could do it this way, however:
var A = function(x){
var that = this;
this.a = x;
this.init = function(){
alert(that.a);
}
}
var a = new A(1);
var b = new A(2)
a.init.call(b);
I have a class that creates an anchor object. When the user clicks on the anchor I want it to run a function from the parent class.
function n()
{
var make = function()
{
...
var a = document.createElement('a');
a.innerHTML = 'Add';
//this next line does not work, it returns the error:
//"this.add_button is not a function"
a.onclick = function() { this.add_button(); }
...
}
var add_button = function()
{
...
}
}
How can I get this done?
Looks like you just need to get rid of the "this." in front of add_button()
You are declaring add_button as a local variable (or private in the weird way that javascript classes work), so it isn't actually a member of "this".
Just use:
a.onclick = function(){add_button();}
The reason it's not working is that this in the context of the onclick function is not the same as this in the context of the n function/"class". If you want this within the function to be equivalent to this from the class, you need to bind this to the function.
Binding is a way of changing the scope of a function -- essentially if you bind to a function, you are replacing the this variable to point to something else. You can read more about binding in Javascript in this alternateidea article.
If you were using prototype, for example, you could do something like:
function n()
{
var make = function()
{
...
a.onclick = function() { this.add_button() }.bind(this);
...
}
}
Which would bind class n's this to the onclick function, thus giving the effect you want.
The "this" in "this.add_button();" is actually referring to the anchor element itself which has no "add_button()" function, if I'm not mistaken.
Perhaps this would work:
a.onclick = function() { n.add_button(); }