5

I try to understand variable scope in Javascript. Here is what I am doing:

<script>
for (var i = 0; i < 3; i++) {
  document.getElementById(i).onclick = function() {
    console.log(i);
  }
}
</script>

The output is always 3, and I understand that's because i has been retained by reference. How do I localise i so it can log incremented value?

Thanks!

update

Thanks guys for quick and decent responses. the solutions are indeed of help!

Initially, I was trying a similar approach to @GrailsGuy, here it is:

<script>
for (var i = 1; i <= 3; i++) {
    document.getElementById(i).onclick = function() {
        console.log(logCall(i));
    }
}
function logCall(i) {
    return i;
}
</script>

But it looks like i isn't being localised. Cannot figure out why!

Michael
  • 2,075
  • 7
  • 32
  • 46
  • Does it have something to do with the onclick event? How could it loop through the onclick if the user only clicks once? And do you need the logCall() function? Wouldn't that just be the same as console.log(i)? – oobie11 Jul 06 '13 at 15:24
  • Regarding your edit, it's the same problem as in your first example. Whether you have `console.log(i)` or `superlongandconfusingfunctionname(i)` inside the event handler doesn't make a difference. You are trying to access `i` after the loop terminated. – Felix Kling Jul 06 '13 at 15:48

2 Answers2

7

Create a new scope

for (var i = 0; i < 3; i++) {
  (function(i) {
    document.getElementById(i).onclick = function() {
      console.log(i);
    }
  }(i));
}
Prinzhorn
  • 22,120
  • 7
  • 61
  • 65
2

In Javascript, scope is not created by brackets (contrary to the C-like syntax). Scope is however, created in functions, so one way is to extract the call into a function (updated):

<script>
for (var i = 0; i < 3; i++) {
    logCall(i);
}

function logCall(i) {
    document.getElementById(i).onclick = function() {
        console.log(i);
    }
}
</script>

This has the added benefit of making the code a bit more reusable.

Igor
  • 33,276
  • 14
  • 79
  • 112
  • I didn't downvote, but your code doesn't solve anything. – Prinzhorn Jul 06 '13 at 14:59
  • 1
    This is incorrect, http://jsfiddle.net/GaCPv/ – Musa Jul 06 '13 at 14:59
  • 5
    This doesn’t work. The `i` in the click handler is still the same as before. `console.log` is a function too, so by your logic it should have worked initially too. – poke Jul 06 '13 at 15:00
  • Bah, yeah sorry, I was typing out a longer explanation and didn't notice I didn't pull out enough of the logic. Should work now. – Igor Jul 06 '13 at 15:02
  • This does not necessarily have something to do with block scope, but also how closures work in JS. If the values of the variables the current environment would be copied to the new environment, instead of them just being linked, then it would work without block scope as well. – Felix Kling Jul 06 '13 at 15:06
  • @FelixKling Yes, valid point. I mention that it's "one way" since I typically prefer to do this, since you can normally find a way to reuse the function too. – Igor Jul 06 '13 at 15:12