Reputation: 104
I have this code :
<!DOCTYPE html>
<html>
<head>
<title>Drag-Drop tests</title>
<meta charset="UTF-8">
</head>
<body>
<script>
var body = document.body;
var cursor = document.createElement("div");
cursor.innerText = "Contenus des fichiers :\n";
cursor.ondragover = e => e.preventDefault();
cursor.ondrop = function(e) {
e.preventDefault();
if (e.dataTransfer.items) {
for (var i = 0; i < e.dataTransfer.items.length; i++) {
if (e.dataTransfer.items[i].kind === "file") {
var file = e.dataTransfer.items[i].getAsFile();
file.cursor = document.createElement("p");
body.appendChild(file.cursor);
file.cursor.innerText = file.name + " contient :";
file.text().then(function(value) {
file.cursor.innerText += " " + value;
});
}
}
}
else {
for(var i = 0; i < e.dataTransfer.files.length; i++) {
var file = e.dataTransfer.files[i];
file.cursor = document.createElement("p");
body.appendChild(file.cursor);
file.cursor.innerText = file.name + " contient :";
file.text().then(function(value) {
file.cursor.innerText += " " + value;
});
}
}
};
body.appendChild(cursor);
</script>
</body>
</html>
As is, if I drop two files on the div element, I get this output :
Contenus des fichiers :
File1.txt contient :
File2.txt contient : Content of file 1 Content of file 2
In file.text().then function, "file" refers to the last file reference declared.
If I replace file.cursor.innerText += by this.cursor.innerText += I get this output :
Contenus des fichiers :
Content of file 1 Content of file 2
File1.txt contient :
File2.txt contient :
In file.text().then function, "this" refers to the first caller, that is, the div itself.
Is there any way to get this :
Contenus des fichiers :
File1.txt contient : Content of file 1
File2.txt contient : Content of file 2
Keeping anonymous nested functions as param for callers.
I know that then does not occurs at the time I define it's callback. I would like to know if I can attach some data to an object and retrieve it on a callback execution.
Thx by advance.
Upvotes: 0
Views: 76
Reputation: 104
Okay,
Another answer pointed out that "var" scopes the function and not the block.
Replacing "var" by "let" anywhere makes the keyword "this" in file.text().then callback not point anything beacuse of the execution flow.
Anyway, "let" makes "file" of each for iteration independant.
Therefore I have this code working :
<!DOCTYPE html>
<html>
<head>
<title>Drag-Drop tests</title>
<meta charset="UTF-8">
</head>
<body>
<script>
let body = document.body;
let cursor = document.createElement("div");
cursor.innerText = "Contenus des fichiers :\n";
cursor.ondragover = e => e.preventDefault();
cursor.ondrop = e => {
e.preventDefault();
let fileLoad = file => {
file.cursor = document.createElement("p");
body.appendChild(file.cursor);
file.cursor.innerText = file.name + " contient :";
file.text().then(value => file.cursor.innerText += " " + value);
}
if(e.dataTransfer.items)
for(let item of e.dataTransfer.items)
if(item.kind === "file") fileLoad(item.getAsFile());
else
for(let file of e.dataTransfer.files)
fileLoad(file);
};
body.appendChild(cursor);
</script>
</body>
</html>
And at the end, I don't really care of code optimisation (for the amount of line code).
I will generate it with some C code.
Upvotes: 0
Reputation: 12637
In file.text().then function, "file" refers to the last file reference declared.
You come from a C background. In JS var
s are function scoped and get hoisted. Better use the newer keywords let
and const
for block scoped variables.
What's the difference between using “let” and “var”?
In file.text().then function, "this" refers to the first caller, that is, the div itself.
this
in JS is notorious; especially for people already having expectations on how it should behave. In JS this
is context-sensitive and depends on how you call a function/method.
How does the “this” keyword work?
Sidenote: I see you wrote some code twice, take a look at Iterators and Generators. I use them to "normalize" whatever is available in dataTransfer
into a sequence of File
s.
var body = document.body;
var cursor = document.createElement("div");
cursor.innerText = "Contenus des fichiers :\n";
// https://mdn.io/Generator
function* getFiles(dataTransfer) {
if (dataTransfer.items) {
// https://mdn.io/for...of
for (let item of dataTransfer.items) {
if (item.kind === "file") {
// https://mdn.io/yield
yield item.getAsFile();
}
}
} else {
// https://mdn.io/yield*
yield* dataTransfer.files;
}
}
cursor.ondragover = e => e.preventDefault();
cursor.ondrop = function(e) {
e.preventDefault();
for (const file of getFiles(e.dataTransfer)) {
const fileCursor = document.createElement("p");
fileCursor.innerText = file.name + " contient :";
body.appendChild(fileCursor);
file.text().then(text => fileCursor.append(text));
}
};
body.appendChild(cursor);
Edit:
I don't even seen any let, const, function*, yeld and yeld*.
In 2020/21 that's basically a fail on the side of the course. const
and let
have been introduced with ES2015
(Javascript version) and are implemented in basically every browser for years.
How would you name such a subtility that consists of adding an asterisk after "fonction" or "yeld" keyword?
The general topic is Iterators and Generators. I've added some specific URLs in the code snippet above to the different keywords.
Upvotes: 1
Reputation: 61
JSFiddle: https://jsfiddle.net/7q9Lphjf/1/
You are getting this result because += " " + value
happens after the promise is executed. In other words, in your example code the following steps happen:
[...] a few moments later
In this particular case it also has to do with the scope of the file
variable which is changed in the for loop, so by the time promise1 is resolved, file
already references the second paragraph, so file.cursor.innerText
refers to the second paragraph's contents.
Instead of :
file.cursor = document.createElement("p");
body.appendChild(file.cursor);
file.cursor.innerText = file.name + " contient :";
file.text().then(function(value) {
file.cursor.innerText += " " + value;
});
Use:
file.text().then(function(value) {
file.cursor = document.createElement("p");
body.appendChild(file.cursor);
file.cursor.innerText = file.name + " contient :";
file.cursor.innerText += " " + value;
});
This solution will however not guarantee any particular order. Whenever the promise is resolved for a file (file reading is complete), only then the contents of that file be added to the div.
If you absolutely need a specific order in which the contents are appended, there are a number of possible solutions to also achieve that, for example promise chaining.
However I'm not sure if this is a requirement, so I'll not make this answer longer than it already is :)
Upvotes: 0
Reputation: 26360
.text()
is asynchronous. All the .then()
occur after the whole for
loop has ended. Very classic asynchronous-operation-in-a-loop problem. You just need to await
instead of using the (old school) .then()
syntax.
cursor.ondrop = async function(e) {
// ...
for(...) {
const value = await file.text();
file.cursor.innerText += " " + value;
}
}
Upvotes: 0