JavaScript for loop on files FileReader

你说的曾经没有我的故事 提交于 2019-12-07 23:22:41

问题


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.


回答1:


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 completed 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 upcoming 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.



来源:https://stackoverflow.com/questions/45012856/javascript-for-loop-on-files-filereader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!