Using closures to capture onclick events - javascript

I'd like to correct this example:
var $foo = $('#foo');
for (var i = 0; i < 10; i++) {
$foo.append($('<li></li>').text(i).on('click', function() {
alert(i);
}));
}
so that it alerts the correct value of i when clicked.
How can I do this? I know that the answer is to use a closure, but I'm not sure how to implement it.
I've tried doing
$foo.append($('<li></li>').text(i).on('click', function() {
return (function(i) {
alert(i);
})();
}));
but that returns undefined.
JSFiddle here: http://jsfiddle.net/tmcw/4phm7/1/

The standard idea is to have an immediately invoked function :
var $foo = $('#foo');
for (var i = 0; i < 10; i++) {
(function(i){
$foo.append($('<li></li>').text(i).on('click', function() {
alert(i);
}));
})(i);
}

You need to create a copy of the i variable for each function:
$foo.append($('<li></li>').text(i).on('click', function(copy_i) {
return (function() {
alert(copy_i);
})(i);
}));

Related

Correct scope reference to object within window object

Since my previous question was not answered i thought i give it another try only formulated better.
(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
//Logs ns0 and ns1 like i want
console.log(ns["someName"]);
window.addEventListener('load', function(){
//Logs ns1 twice
console.log(ns["someName"]);
});
}
})();
So the question being how to keep the correct scope within the window eventListener.
Thanks!
You can use the magic of closures
(function(value) {
window.addEventListener('load', function(){
//Logs ns1 twice
console.log(value);
});
})(ns["someName"]);
Defined an inline function and called it inmediatly using the current value for ns["someName"]
I simplified the code to show how you can 'preserve' the scope within each load-callback-function. You can use a self-execution function to return a closure:
(function() {
for(var i = 0; i < 2; i++) {
window.addEventListener('load', (function(i){
return function(evt) {
// i is as expected: 0 and then 1
console.log(i);
// the variable 'evt' contains the event-object
};
})(i));
}
})();
thank you all for your input much appreciated! ive been battling this for a two days now and i was about to pull out my hair.
here is the final code:
(function() {
for(var i = 0; i < 2; i++){
var ns = {};
ns['someName' + i] = 'ns' + i;
//Logs ns0 and ns1 like i want
console.log(ns);
(function(value) {
window.addEventListener('load', function(event){
//Logs ns0 and ns1 like i want
console.log(value);
//preseved my events
console.log(event);
});
})(ns);
}
})();
(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
console.log(ns["someName"]);
(function(value){
window.addEventListener('load', function(){
console.log(value);
});
}(ns['someName'] ));
}
})();
You can achieve it with the help of closures
(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
console.log(ns["someName"]);
(function(value){
window.addEventListener('load', function(){
return (function(){
console.log(value);
})()
});
}(ns["someName"]));
}
})();
This will do the trick :)

How to append two string or number for the function name in javascript

for(i=0;i< 6; i++ ){
function myFunc + 1() {
alert(i);
}
}
myFunc1();
This is my code I want to create function from myFunc1 to myFunc6, but it seems like it does not work, why??
for (var i = 0; i < 6; i++) {
window['myFunc' + i] = (function(i) {
alert(i);
})(i);
}
you can do like this :
var myFuncs = [];
for ( i=0; i<6; i++ ) {
(function(i){ myFuncs.push(function() { alert(i) }); })(i);
}
myFuncs[0]();

get parameter of api callback

here is what i've got
var mods = this.registry.gmmods;
for (var i = 0; i < mods.length; i++) {
if(mods[i] != this.config.botid){
this.api.stalk(mods[i],true,function (data){
console.log(mods[i]);
});
}
}
only the console log outputs undefined and i can seem to figure out how to get that data in callback function as the callback data doesn't contain it
could anyone tell me how i might be able to do that
It's a problem with i in your closure, when callback of this.api.stalk is called chances are that i is mods.length. See the following example:
var i = 0;
var arr=["hi","there"];
for(i=0;i<arr.length;i++){
setTimeout(function(){
console.log(arr[i]); //undefined
console.log("and i is:"+i); //i will be 2
},100);
}
Here is how you can solve the closure problem:
var i = 0;
var arr=["hi","there"];
for(i=0;i<arr.length;i++){
setTimeout(
(function(index){
return function(){
console.log("Index is:"+index);//0 and 1
console.log("arr at index:"+arr[index]);//hi and there
console.log("i is:"+i);//2 and 2
console.log("arr at i:"+arr[i]);//undefined and undefined
}
})(i)
,100);
}
Your code could look something like:
var mods = this.registry.gmmods;
for (var i = 0; i < mods.length; i++) {
if(mods[i] != this.config.botid){
this.api.stalk(mods[i],true,
(function(index){
return function (data){
console.log("index is:"+index);
console.log(mods[index]);
console.log("i is:"+i);
console.log(mods[i]);
}
})(i)
);
}
}

Javascript function doesn't receive an argument correctly [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript closures and variable scope
Assign click handlers in for loop
I have this script:
var MyClass = {
MyArray: new Array(0, 1, 2, 3, 4),
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
var cen = document.getElementById("cen_" + i); // It is an img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc1(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}
},
MyFunc2: function(id) {
alert(id);
}
}
My problem is that, at this line :cen.onclick = function() { MyClass.MyFunc1(i); }; the argument sent to MyFunc2 is always -1. The MyFunc1 function should create four images, each one with an onclick event. When you click on each image, the MyFunc2 function should show the corresponding i value. It looks like the i value is not "saved" for each event and image element created, but only its "pointer".
Thanks!
You should be familiar with the concept of JavaScript closures to understand why this happens. If you are, then you should remember that every instance of the
function() { MyClass.MyFunc1(i); };
function closure contains i's value of -1 (since it is the final value of this variable after the entire loop finishes executing.) To avoid this, you might either use bind:
cen.onclick = (function(i) { MyClass.MyFunc1(i); }).bind(null, i);
or use an explicitly created closure with the proper i value.
It's a normal case and misunderstand of closures, see this thread and you may get some clue, the simply way to fix this problem is to wrap your for loop body with an Immediate Invoked Function Expression
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}(i));
}
}
You are capturing a variable that changes inside the loop, so you always get the last value of i.
You can easily fix that by creating a closure:
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
})(i);
}
},

Accessing variables w/in complete function

for (var i = 0; i < 32; i++) {
var thisId = dropId+i;
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
}
In the above code example, by the time I get around to executing animate's complete function, thisId represents the last assigned value from the for loop NOT the value that I wanted to pass in for each iteration of the loop. Is there a way to get it to access the correct thisId?
JavaScript does not have block scope. You can create a new scope by calling a function. E.g.
for (var i = 0; i < 32; i++) {
(function(thisId) {
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
}(dropId+i)); // <-- calling the function expression and passing `dropId+i`
}
Variables declarations area always hoisted to the top of the function. So even if you have the declaration inside the loop, it is actually the same as:
var i, thisId;
for(...) {
thisId = dropId + i;
//...
}
Every closure you create inside the loop references the same thisId. It's like in Highlander: "There can be only one."
You need to use a closure around the current thisId.
for (var i = 0; i < 32; i++) {
var thisId = dropId+i,
complete = (function(id) {
return function() {
if ($("#p"+id).position().left == 1024) {
$("#p"+id).remove();
window.console.log("removed");
}
}
}(thisId));
$("#p"+thisId).animate({ left:"+=32px" }, complete);
}
Just wrapping what you had in an anonymous function should work:
for (var i = 0; i < 32; i++) {
(function() {
var thisId = dropId+i;
$("#p"+thisId).animate({ left:"+=32px" }, function(){
if ($("#p"+thisId).position().left == 1024) {
$("#p"+thisId).remove();
window.console.log("removed");
}
});
})();
}

Categories

Resources