2

I can see an issue in the following code:

var elems = {
    'elem1': 'param1fds',
    'elem2': 'paramaafds2',
    'elem3': 'paramfdsfd3fdsfds'
  };

for (var k in elems) {
  $('#' + k).click(function(e) {
  // k is always elem3 when I click on the element
  // elems[k] == 'paramfdsfd3fdsfds'

By the time the code is getting executed, k is equal elem3. How can I deal with that?

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
Incerteza
  • 32,326
  • 47
  • 154
  • 261
  • As the code uses jQuery, I've added the tag to your question. If you feels it denatures it, fee free to remove it. – Denys Séguret May 18 '15 at 14:20
  • possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Evan Davis May 18 '15 at 14:57

3 Answers3

6

The standard generic solution is to add an IIFE protecting your variable in a closure:

for (var k in elems) {
   (function(k){
     $('#' + k).click(function(e) {
        // k is always elem3 when I click on the
     })
    })(k);
}

As you're using jQuery, you could also use $.each:

$.each(elems, function(k){
     $('#' + k).click(function(e) {

And a third solution would be to use on and provide the data:

for (var k in elems) {
    $('#' + k).on('click', {k:k}, function(e) {  
         // use e.data.k here

In the near future, with ES6, a fourth solution will be block scoped variables using let:

for (let k in elems) {
   $('#' + k).click(function(e) {
      // k is OK
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • thats called a self-executing function. – Daniel A. White May 18 '15 at 13:52
  • The important point is to wrap a new function around the variable. JavaScript is scoped with functions, so the new function's variable captures the value of the variable when the loop is run. – Katie Kilian May 18 '15 at 13:53
  • what's this behaviour called in js? – Incerteza May 18 '15 at 14:46
  • What behavior ? Your problem is due to asynchronicity (or rather the k variable being "free") and you fix it by creating a new "scope" protecting your variable (in JS the scope of a non global variable is the function call, and functions declared in a scope recursively access variables in the scope). – Denys Séguret May 18 '15 at 14:49
  • The concept that you might want to dive into is called "scope" (and I just thought about a new fourth solution...). – Denys Séguret May 18 '15 at 14:52
  • @dystroy is the 4th solution the one I just posted? – Evan Davis May 18 '15 at 14:56
  • @Mathletics no, see edit, my fourth solution is ES6 based. Your solution is too verbose to my taste to enter my list, especially when OP uses jQuery, I don't like the idea of creating an array just for iterating properties. But of course it also works, no problem. – Denys Séguret May 18 '15 at 14:57
  • @dystroy hmm, to each their own. – Evan Davis May 18 '15 at 14:59
1
var elems = {
    'elem1': 'param1fds',
    'elem2': 'paramaafds2',
    'elem3': 'paramfdsfd3fdsfds'
  };

for (var k in elems) {
    (function(elem){
        $('#' + elem).click(function(e) {});
    })(k);
...
}
AmmarCSE
  • 30,079
  • 5
  • 45
  • 53
0

I'd argue that the modern solution to this is to do it functionally, rather than with a loop:

Object.keys(elems).forEach( function(elKey) {
    $('#' + elems[elKey]).click(function(e) {});
});

It's the same result as using an IIFE but is, I think, a little easier to follow.

Evan Davis
  • 35,493
  • 6
  • 50
  • 57