3

I have a little problem with one of my javascript code. Here is the code

//assume array is an array containing strings and myDiv, some div in my doc
for(var i in array) {
    var myString = array[i];
    var a = document.createElement('a');
    a.innerHTML = myString;
    a.addEventListener("click", function() {myFunc(myString)}, false);
    myDiv.appendChild(a)
}
function myFunc(s) {alert(s);}

However, since Strings are passed by reference in JavaScript, I see always the last string of my array when I click on the link a in question. Thus, my question is "How can I pass myString by value ?". Thank you for your help ! Phil

user1553136
  • 328
  • 5
  • 17
  • 4
    never iterate over an array using `for...in`. – jbabey Aug 13 '12 at 15:11
  • _MY EYES_ `addEventListener` allows you to _delegate_ an event, rather then binding it to each and every element individually, you're doing both and therefore neither: the worst of two worlds... + `addEventListener` isn't supported by IE <9 – Elias Van Ootegem Aug 13 '12 at 15:18
  • @EliasVanOotegem you're right about the delegation, although using a delegated handler still leaves him with the problem of how to find the appropriate data for the target of the event. – Alnitak Aug 13 '12 at 15:19
  • I know, that's why I merely commented your solution would be my suggestion. With IE<9 supported: `if (a.addEventListener){a.addEventlistener('click',make_callback(myString),false);} else { a.attachEvent('onclick',make_callback(myString));}` – Elias Van Ootegem Aug 13 '12 at 15:29

3 Answers3

2

Primitive variables are not passed by reference in Javascript.

This is the classic 'loop variable called inside a closure' problem.

Here's one commonly-used solution to that problem:

for (var i = 0, n = array.length; i < n; ++i) {
    var myString = array[i];
    var a = document.createElement('a');
    a.innerHTML = myString;
    a.addEventListener("click", make_callback(myString), false);
    myDiv.appendChild(a)
}

function make_callback(s) {
     return function() {
         alert(s);
     }
}

Note that this isn't particularly memory efficient since it creates a new function scope for every element in the array.

A better solution might be to store the variable data actually on the element (i.e. as a new property) and retrieve that in the callback. In fact you're already storing that string in the .innerHTML property so you could just read that and then take advantage of delegation to register just the one handler on the elements' parent:

for (var i = 0, n = array.length; i < n; ++i) {
    var a = document.createElement('a');
    a.innerHTML = array[i];
    myDiv.appendChild(a)
}

myDiv.addEventListener('click', function(ev) {
    alert(ev.target.innerHTML);
}, false);
Alnitak
  • 334,560
  • 70
  • 407
  • 495
2

You should add a closure around your event handler:

JavaScript closure inside loops – simple practical example

a.addEventListener("click", function (s) {
    return function () {
        alert(s)
    };
}(myString), false);

Also, you should not use for...in loops on arrays.

Community
  • 1
  • 1
jbabey
  • 45,965
  • 12
  • 71
  • 94
-1

try this : i think its always good not practice to iterate array using for...in

for(var i=0; i<array.length;i++) {
    var myString = array[i];
    var a = document.createElement('a');
    a.innerHTML = myString;
    a.addEventListener("click", function() {myFunc(myString)}, false);
    myDiv.appendChild(a)
}
function myFunc(s) {alert(s);}
Shreedhar
  • 5,502
  • 3
  • 22
  • 27