0

The Javascript/jQuery code below will output three divs with the numbers 1, 2 and 3. However when clicking any of the divs it will alert the number 3 always.

How would I write it so that it will alert the "correct" number. Without putting the number into the HTML itself.

var values = [1, 2, 3];

for (var i in values) {
    var element = $("<div>" + values[i] + "</div>");

  $("body").append(element);

  element.click(function() { alert(values[i]); });
}
lox
  • 1,602
  • 4
  • 27
  • 41
  • 1
    Would you be comfortable storing the data not as the html string but as jQuery data attached to the element itself? – Alexander Nied Oct 13 '16 at 13:05
  • 3
    That's a closure issue. At time you click and call handler, the `i` as last value. – A. Wolff Oct 13 '16 at 13:06
  • Also, you shouldn't use a `for...in` loop for an array-- it is only meant to be used for iterating over keys in an object. If you need to iterate over an array, use a standard `for` loop, or `forEach` if you're so inclined. – Alexander Nied Oct 13 '16 at 13:06
  • A better dupe would have been http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – epascarello Oct 13 '16 at 13:07
  • try this instead: `var element = $("
    ",{"class":"clickme","data-value":values[i]}).text(values[i]);` and add `$("body").on("click",".clickme",function() { alert($(this).data("value"));});`
    – mplungjan Oct 13 '16 at 13:08
  • @epascarello I actually do not agree. The dupe I posted shows the exact code to use, whereas your dupe is a huge post with much information that is worth reading but harder to match to the problem at hand – mplungjan Oct 13 '16 at 13:10

4 Answers4

1

This should solve it:

var values = [1, 2, 3];

function clickHandler(event) {
  alert(event.target.innerHTML);
}

for (var i in values) {
    var element = $("<div>" + values[i] + "</div>");

  $("body").append(element);

  element.click(clickHandler);
}
gauravmuk
  • 1,606
  • 14
  • 20
  • You beat me by a couple of seconds. Your code works. :) https://jsfiddle.net/j7q0vswL/ – Neo Oct 13 '16 at 13:08
  • 1
    But what if for some reason (?!) the appended element doesn't only contains the `values[i]`??? This is really not a reliable way to handle it. You'd have better at least to set some custom data attribute – A. Wolff Oct 13 '16 at 13:10
  • it's all about perspective. you saw it from a different view and i saw it from a different view :) – gauravmuk Oct 13 '16 at 13:12
  • @jonLuci But the question wasn't how to get appended DIVs content :) I guess it was just a minimalistic sample OP posted – A. Wolff Oct 13 '16 at 13:14
1

So, to avoid closure issue, you could wrap your binding inside a IIFE:

var values = [1, 2, 3];

for (var i in values) {
  var element = $("<div>" + values[i] + "</div>");

  $("body").append(element);

  (function(i) { // 'i' is local to anonymous function block
    element.click(function() {
      alert(values[i]);
    });
  }(i));
}
A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • this could be solved even without closure :) – gauravmuk Oct 13 '16 at 13:10
  • Since the OP question is obviously a simplified example, this trick to avoid closure effect is good to know. – Louys Patrice Bessette Oct 13 '16 at 13:15
  • @LouysPatriceBessette Ya, but the best one (when fully implemented by all browsers) would be to use function scope, using `let`: https://jsfiddle.net/sa1L2ckr/ Now actually, only Opera Mini doesn't implement it, if that really matters – A. Wolff Oct 13 '16 at 13:17
0

Try this:

var values = [1, 2, 3];
for (var i in values) {
  var element = $("<div>" + values[i] + "</div>");
  $("body").append(element);

}

$("div").click(function() { 
   alert($(this).text()); 
});

Hope it will be useful

priya_singh
  • 2,478
  • 1
  • 14
  • 32
0
var values = [1, 2, 3];

for (var i in values) {
    var element = $("<div>" + values[i] + "</div>");

  $("body").append(element);

  element.click(function() { alert(this.innerHTML); });
}

The most simple solution: Read .innerHTML