最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript for loop on files FileReader - Stack Overflow

programmeradmin0浏览0评论

The problem is breaking my mind. Can someone help me? In the <script> tag in my html file I have this:

window.ondragover = function(e){return false;}
window.ondragenter = function(e){return false;}
window.ondrop = function(e){
    var files = e.target.files || e.dataTransfer.files;
    for (var i = 0, file; file = files[i];i++){
        var img = document.createElement('img');
        img.height = 200;
        img.width = 200;
        img.style.background = 'grey';
        document.body.appendChild(img);
        var reader = new FileReader();
        reader.onload = function(){
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
    }
    return false;
}

but when I drop several image files on the browser, only the last image file is loaded and displayed in the last img element, others stay grey.

The problem is breaking my mind. Can someone help me? In the <script> tag in my html file I have this:

window.ondragover = function(e){return false;}
window.ondragenter = function(e){return false;}
window.ondrop = function(e){
    var files = e.target.files || e.dataTransfer.files;
    for (var i = 0, file; file = files[i];i++){
        var img = document.createElement('img');
        img.height = 200;
        img.width = 200;
        img.style.background = 'grey';
        document.body.appendChild(img);
        var reader = new FileReader();
        reader.onload = function(){
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
    }
    return false;
}

but when I drop several image files on the browser, only the last image file is loaded and displayed in the last img element, others stay grey.

Share Improve this question edited Jul 10, 2017 at 14:09 msanford 12.3k13 gold badges71 silver badges98 bronze badges asked Jul 10, 2017 at 13:04 Stas ShepelevStas Shepelev 1651 silver badge12 bronze badges 2
  • 1 Have a feeling this is going to be due to your use of img within the loop. Since reader.onload is async, the for loop has already pleted and img points to the last one – chazsolo Commented Jul 10, 2017 at 13:15
  • in the 1st loop img is asigned to a new element and then its src is asigned to results of new reader that was created in the same loop. next loop: img var is reasigned to a new element and its src is asigned to the result of another new reader. is it wrong? – Stas Shepelev Commented Jul 11, 2017 at 4:09
Add a ment  | 

1 Answer 1

Reset to default 11

As @chazsolo mentioned:

Have a feeling this is going to be due to your use of img within the loop. Since reader.onload is async, the for loop has already pleted and img points to the last one

You can fix this by using let instead of var within the loop (let - MDN). This will give each img and reader a block scope within the loop, allowing the async reader method to still access the actual value from that specific loop run.

window.ondragover = function(e){return false;}
window.ondragenter = function(e){return false;}
window.ondrop = function(e){
    var files = e.target.files || e.dataTransfer.files;
    debugger;
    for (var i = 0, file; file = files[i];i++){
        let img = document.createElement('img');
        img.height = 200;
        img.width = 200;
        img.style.background = 'grey';
        document.body.appendChild(img);
        let reader = new FileReader();
        reader.onload = function(){
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
    }
    return false;
}

Update: var vs let

So why is it not working as suspected with var? I try to explain the difference of let and var with a few practical examples.

Variable declarations, wherever they occur, are processed before any code is executed.

This leads us to the following example (don't mind the error in the end, which is produced by the snipped plugin):

Declaration with var

/**
In this example, 'a' is declared after it is used. This doesn't matter, as the 
declaration of 'a' will be processed before running the code. This means a is 
initialized with 'undefined' but is valid. Also the scope of a lies within the 
execution context, that's why it is even available outside of the loop. 
**/
console.log("---------");
console.log("Example Declaration var");
console.log("---------");
for (var i = 0; i < 2; i++) {
  console.log(a); // a is declared but undefined on the 1st run, afterwards it get redeclared and owns the value from the last run.
  var a = i;
}
console.log(a); // a is even available out here as still same execution context.
We see, that on every re declaration of a the value of the a before, is kept. It is not a new "instance".

So what's happening if we use a async function within the loop?

Async function with var

/**
This example shows you the effects, if you use a async function within a loop. 
As the loop will be executed way under 100 miliseconds (which is the time out 
of the async function), c will have the same value for all exections of the 
async mehtod, which is the value assigned by the last run of the loop.
**/
console.log("---------");
console.log("Example effects async var");
console.log("---------");
for (var i = 0; i < 2; i++) {
  var c = i;
  setTimeout(function() {
    console.log(c); //var does redeclare, therefor c will be modified by the next loop until the last loop.
  }, 100);
}

Exactly, always the same output (adapted to your problem, always the same img element and file)

Let's see what's happening with let

Declaration with let

/**
In this example, 'b' is declared after it is used with let. let will be processed 
during runtime. This means 'b' will not be declared when used. This is an invalid 
state. let will give a strict context within the loop. It will be not available 
outside. let has a similar behavior as a declaration in Java.
**/
console.log("---------");
console.log("Example Declaration let");
console.log("---------");
for (var i = 0; i < 2; i++) {
  try {
    console.log(b); //b is not declared yet => exception
  } catch (ex) {
    console.log("Exception in loop=" + ex);
  }
  let b = i;
  console.log("Now b is declared:"+b);
}
try {
  console.log(b); // b is not available out here as the scope of b is only the for loop. => exception
} catch (ex) {
  console.log("Exception outside loop=" + ex);
}
console.log("Done");
A lots of exceptions are thrown, as let has a more specific scope. Which leads to more intentional coding.

Finally, we see what happens when we use let and a async function within the loop.

Async function with let

/**
This example shows you the effects, if you use a async function within a loop. 
As the loop will be executed way under 100 milliseconds (which is the time out 
of the async function). let declares a new variable for each run of the loop, 
which will be untouched by uping runs.
**/
console.log("---------");
console.log("Example effects async let");
console.log("---------");
for (var i = 0; i < 2; i++) {
  let d = i;
  setTimeout(function() {
    console.log(d); //let does not redeclare, therefor d will not be modified by the next loop
  }, 100);
}

Conclusion

In your example, you always end up with the last assigned img element and the last assigned file. Your doing the same operation as many times as you have file in your array for the only the last file.

发布评论

评论列表(0)

  1. 暂无评论