2
for (var i=0, file; file=files[i]; i++) {
    if (file.type=='image/jpeg') {
        var reader = new FileReader();
        reader.onload = function(e) {
            $('#upload_preview').append('<img src="'+e.target.result+'" width="150" alt="" />');
            alert(file.type); // error - file is undefined
        }
        reader.readAsDataURL(file);
    }
}

Variable reader was declared within the code block where file was defined, yet the error reports variable file is undefined. this is driving me crazy, and I know it's something really basic.

indiv
  • 17,306
  • 6
  • 61
  • 82
L84
  • 974
  • 2
  • 11
  • 21
  • You have a missing quote on the first line. Mistake? – Some Guy Sep 02 '12 at 09:06
  • Nah it's not that, I messed up there when I was simplifying the example. – L84 Sep 02 '12 at 09:07
  • @L84, Where did you declared file variable? – Kundan Singh Chouhan Sep 02 '12 at 09:08
  • 1
    Can't say I'm an expert on js, but your onload function is called at a later point (i.e. after load). Isn't file out of scope then? – Qiau Sep 02 '12 at 09:08
  • @Qiau It's in scope, and it could be set to `null` when the callback is executed, but then again it wouldn't say "undefined". – MaxArt Sep 02 '12 at 09:10
  • 3
    @Qiau: The function creates a closure that keeps file in scope, but I think you're on the right track in that the file reference gets modified somehow by the time the function gets called. – indiv Sep 02 '12 at 09:10
  • +1 indiv, as long as the handler is bound as in OP's example code, the handler function will have access to all variables in the outer scope. To enter in the `if` block which sets the handler, `file` must be defined so there must be an error somewhere else. – Fabrício Matté Sep 02 '12 at 09:12
  • @KundanSinghChouhan in the for loop just above the code block I posted - `for (var i=0, file; file=files[i]; i++)` but should it matter? It's defined in that block, unless... the for loop is completed by the time onload event is fired? – L84 Sep 02 '12 at 09:13
  • As @indiv points out the file variable seems set to undefined in some parts of the code. Please show us the whole implementation otherwise its hard to say whats wrong here. – Andreas Köberle Sep 02 '12 at 09:14
  • L84, sure matter. The for.. loop must be not in the same closure. – Marcelo De Zen Sep 02 '12 at 09:15
  • @AndreasKöberle It's maybe 800 lines, mixed with HTML and PHP. I don't think it would clarify things much. I've added the outer for loop. – L84 Sep 02 '12 at 09:16

1 Answers1

3

So this loop for (var i=0, file; file=files[i]; i++) mention in the comments will set file to undefined in the last loop. You should use each or call a function in the for loop to save scope.

files.forEach(function(file){
if (file.type=='image/jpeg') {
    var reader = new FileReader();
    reader.onload = function(e) {
        $('#upload_preview').append('<img src="'+e.target.result+'" width="150" alt="" />');
        alert(file.type); // error - file is undefined
    }
    reader.readAsDataURL(file);
}
})

or if you need to support older browser:

for (var i=0, l=files.length; i < l; i++) {
    (function(i) {
        var file = files[i];
        if (file.type == 'image/jpeg') {
            var reader = new FileReader();
            reader.onload = function(e) {
                $('#upload_preview').append('<img src="' + e.target.result + '" width="150" alt="" />');
                alert(file.type); // error - file is undefined
            }
            reader.readAsDataURL(file);
        }
    })(i);
}
Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • Error: files.each is not a function. Also, it's undefined in every loop. – L84 Sep 02 '12 at 09:20
  • @L84: The magic google keywords you want to search for are "javascript closure loop". E.g., http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – indiv Sep 02 '12 at 09:22
  • Sorry must be `forEach` not `each` – Andreas Köberle Sep 02 '12 at 09:24
  • as @indiv said, you'll be overwriting the `reader` var in each iteration if you don't use a closure, which the `forEach` creates. I just fail to see how OP's original `for` loop would have an undefined `file` in the last iteration. The conditional part `file=files[i]` would be false when `files[i]` is `undefined` right? Maybe I'm too sleepy. – Fabrício Matté Sep 02 '12 at 09:28
  • Still the same error. Javascript doesn't even have a forEach, and when I used the jQuery $.each, there was still the original error (file is undefined) – L84 Sep 02 '12 at 09:29
  • Well, just goes to show I don't know jack about Javascript. Still, the error is `TypeError: files.forEach is not a function` so... – L84 Sep 02 '12 at 09:32
  • 1
    @FabrícioMatté the loop runs until the `file=files[i]` is falsy. So when it stops file muts be `undefined` – Andreas Köberle Sep 02 '12 at 09:33
  • Yes I worded it 'til there. Now that you said it I realized that the `file` var in that scope will then be set to `undefined` and that will thrown an error when the onload handler executes. I see it clearly now. – Fabrício Matté Sep 02 '12 at 09:34
  • @AndreasKöberle but see that alert() call, that should alert on every loop, not just the last one when it's undefined, right? – L84 Sep 02 '12 at 09:35
  • @L84 updated the answet with version older browser, that don't support `Array.forEach` – Andreas Köberle Sep 02 '12 at 09:39
  • @AndreasKöberle Using Firefox 15.0 :) This is just too complicated, I'll mark your answer as accepted, maybe I'll go back to it in a couple of months when I understand any of this. – L84 Sep 02 '12 at 09:40
  • @L84 Nope. You're overwriting the `reader` with each iteration, so only the last one will still be in the memory. Using a closure such as the Andreas' updated answer should take care of that now. – Fabrício Matté Sep 02 '12 at 09:41
  • @FabrícioMatté I didn't understand any of that. :) Thanks anyway, I'll skip this problem and come back to it some other time. – L84 Sep 02 '12 at 09:44
  • @L84 Well yeah, you can add [this](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) to your favorites for the next time you have a closure-related problem, by the 10~20th time you read all those answers you start to understand a bit about closures. `=]` – Fabrício Matté Sep 02 '12 at 09:46