JS Multiple Image Upload And Preview AND Write out it's properties - javascript

I have this code that allows you to upload multiple images and preview it. I also want to get the images name and their size. Its only works while you upload only one image. If you upload more than one, the image properties will be the same for each images. (They'll get the last ones infos.)
I think the problem is in the picReader function, because it bugs with the for loop. Any idea how to sole it?
What I want: I want to get the file.name which is the name of the image (e.x: example.jpg) and the file.size which is the size of the image (e.x: 181135) for each images one by one with their own infos. But now, it will display the same infos for each images (= It will show the last image's infos for the others too).
function handleFileSelect() {
if (window.File && window.FileList && window.FileReader) {
document.getElementById('result').textContent = '';
var files = event.target.files; //FileList object
var output = document.getElementById("result");
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (!file.type.match('image')) continue;
var picReader = new FileReader();
picReader.addEventListener("load", function (event) {
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
console.log(file.name+'::'+file.size);
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
}
} else {
console.log("Your browser does not support File API");
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
body {
font-family:'Segoe UI';
font-size: 12pt;
}
header h1 {
font-size:12pt;
color: #fff;
background-color: #F39C12;
padding: 20px;
}
article {
width: 80%;
margin:auto;
margin-top:10px;
}
.thumbnail {
height: 100px;
margin: 10px;
}
output {
display: flex;
}
<article>
<label for="files">Select multiple files:</label>
<input id="files" type="file" multiple="multiple" />
<output id="result" />
</article>

The problem is that i, inside all anonymous functions which you pass as the load event listener, has the same variable outside of the function which makes the file variable to be same for all iterations as well.
You have 3 options here:
ES6:
If you are using ES6, you can use let instead of var. Each iteration of the loop with a let index, has a new variable i:
for (let i = 0; i < files.length; i++) {
let file = files[i];
if (!file.type.match('image')) continue;
var picReader = new FileReader();
picReader.addEventListener("load", function (event) {
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
console.log(file.name+'::'+file.size);
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
}
forEach method:
files.forEach(function(file) {
if (!file.type.match('image')) continue;
var picReader = new FileReader();
picReader.addEventListener("load", function (event) {
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
console.log(file.name+'::'+file.size);
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
})
A function that creates another function: With this solution, you would bind the variable inside each function to a value that does not change:
for (let i = 0; i < files.length; i++) {
let file = files[i];
if (!file.type.match('image')) continue;
var picReader = new FileReader();
picReader.addEventListener("load", createFunc(i, files));
picReader.readAsDataURL(file);
}
function createfunc(i, files) {
return function(event) {
var file = files[i];
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
console.log(file.name+'::'+file.size);
output.insertBefore(div, null);
});
}

Related

Javascript: How I make fileupload (not only image)?

I made a file uploader that handles images, but it doesn't work well with files.
For example: if I upload 3 xlsx/word or any files, these will have the same name for each.
My code is here:
<form>
<input id="files" type="file" multiple="multiple">
<div id="result"></div>
</form>
function handleFileSelect(event) {
if (window.File && window.FileList && window.FileReader) {
var files = Array.from(event.target.files);
var output = document.getElementById("result");
output.innerHTML = '';
console.log(files);
for (var i = 0; i < files.length; i++) {
var file = files[i];
if(file.type.match('.php')){
alert('ERROR');
continue;
}
var picReader = new FileReader();
picReader.addEventListener("load", function (event) {
var picFile = event.target;
var div = document.createElement("div");
div.className = "col-6 col-sm-4 p-1";
if (file.type.match('image')) {
div.innerHTML = "<img src='" + picFile.result + "'" + "title='" + file.name + "'/>";
}else{
div.innerHTML = "<div class='upload-thumb'>" + file.name + "</div>";
}
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
}
} else {
console.log("Your browser does not support File API");
}
}
Link:
https://jsfiddle.net/laszlooo/7c1Lv5x2/
Thank You!
Problem you have is you have the infamous for loop bug. Where the reference to i updates as your loop executes. You can either use let instead of var or break out the part you read the file into a function so you do not have the issue.
function readFile(file, output) {
var picReader = new FileReader();
picReader.addEventListener("load", function(event) {
var picFile = event.target;
var div = document.createElement("div");
div.className = "col-6 col-sm-4 p-1";
if (file.type.match('image')) {
div.innerHTML = "<img src='" + picFile.result + "'" + "title='" + file.name + "'/>";
} else {
div.innerHTML = "<div class='upload-thumb'>" + file.name + "</div>";
}
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
}
function handleFileSelect(event) {
if (window.File && window.FileList && window.FileReader) {
var files = Array.from(event.target.files);
var output = document.getElementById("result");
output.innerHTML = '';
console.log(files);
for (var i = 0; i < files.length; i++) { // <-- where the problem begins
var file = files[i]; // <-- this is your problem with the reference
if (file.type.match('.php')) {
alert('ERROR');
continue;
}
readFile(file, output) // <-- using a function gets around the reference issue
}
} else {
console.log("Your browser does not support File API");
}
}
document.querySelector("input").addEventListener("change", handleFileSelect)
<form>
<input id="files" type="file" multiple="multiple">
<div id="result"></div>
</form>

Access for loop data inside eventlistener [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
I've been trying to create a snippet in order to upload multiple files. The code below works but I need to get my image id in order to clear my filesCollection variable when the user removes the image.
Since the event listener load all files altogether, the variable file.name always show the same value for my dynamically created images.
Any insight that could help me?
Thanks in advance
for (var i = 0; i < files.length; i++) {
var file = files[i];
var picReader = new FileReader();
picReader.addEventListener("load", function (event) {
var picFile = event.target;
var div = document.getElementById("uploadboard");
div.innerHTML = div.innerHTML + "<span class='preview'>
<img id='"+ file.name +"' src='"+ picFile.result +" '/>
<button class='remover'></button></span>";
}
picReader.readAsDataURL(file);
filesCollection[file.name] = file;
}
Because of the statement var
The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global.
You can create a function using an approach with IIFE (Immediately Invoked Function Expression)
for (var i = 0; i < files.length; i++) {
var f = files[i];
var picReader = new FileReader();
picReader.addEventListener("load", (function (file) {
return function(event) {
var picFile = event.target;
var div = document.getElementById("uploadboard");
div.innerHTML = div.innerHTML + "<span class='preview'>
<img id='"+ file.name +"' src='"+ picFile.result +" '/>
<button class='remover'></button></span>";
}
)(f);
}
picReader.readAsDataURL(f);
filesCollection[f.name] = f;
}
One more alternative is using the statement let
The let statement declares a block scope local variable, optionally initializing it to a value.
for (let i = 0; i < files.length; i++) {
let file = files[i];
let picReader = new FileReader();
picReader.addEventListener("load", function (event) {
let picFile = event.target;
let div = document.getElementById("uploadboard");
div.innerHTML = div.innerHTML + "<span class='preview'>
<img id='"+ file.name +"' src='"+ picFile.result +" '/>
<button class='remover'></button></span>";
}
picReader.readAsDataURL(file);
filesCollection[f.name] = file;
}

Remove button is not working

I'm trying to add preview and delete option before uploading multiple images, this is what I found:
$(document).ready(function() {
if (window.File && window.FileList && window.FileReader) {
$("#files").on("change", function(e) {
var files = e.target.files,
filesLength = files.length;
for (var i = 0; i < filesLength; i++) {
var f = files[i]
var fileReader = new FileReader();
fileReader.onload = (function(e) {
var file = e.target;
$("<span class=\"pip\">" +
"<img class=\"imageThumb\" src=\"" + e.target.result + "\" title=\"" + file.name + "\"/>" +
"<br/><span class=\"remove\">Remove image</span>" +
"</span>").insertAfter("#files");
$(".remove").click(function(){
$(this).parent(".pip").remove();
});
});
fileReader.readAsDataURL(f);
}
});
} else {
alert("Your browser doesn't support to File API")
}
});
But while uploading all images are getting uploaded, how to resolve this? I'm using php.
Ok, i'm create example that solved your problem:
Your HTML
<form method="post" id="sendForm">
<input type="file" id="files" multiple>
<input type="submit">
</form>
Your JS
$(document).ready(function() {
if (window.File && window.FileList && window.FileReader) {
// Array which stores all selected images
var sendData = [];
$("#files").on("change", function(e) {
var files = e.target.files,
filesLength = files.length;
for (var i = 0; i < filesLength; i++) {
var f = files[i]
var fileReader = new FileReader();
fileReader.onload = (function(e) {
var file = e.target;
$("<span class=\"pip\">" +
"<img class=\"imageThumb\" src=\"" + e.target.result + "\" title=\"" + file.name + "\"/>" +
"<br/><span class=\"remove\">Remove image</span>" +
"</span>").insertAfter("#files");
// Add all images to array
sendData.push({file: file, url: e.target.result});
$(".remove").click(function() {
var self = $(this).parent().children();
sendData.map(function(value, currentIndex, data) {
// Remove only image which removed from preview
if (self[0].currentSrc === value.url) {
sendData.splice(currentIndex, 1);
}
});
$(this).parent().remove();
});
$("#sendForm").submit(function(e) {
// Finally post all data to your PHP url that
$.post("your/php/url", sendData);
});
});
fileReader.readAsDataURL(f);
}
});
} else {
alert("Your browser doesn't support to File API")
}
});

How to give an image a different id each time it gets uploaded

I'm currently testing out a piece of code by user OGiS0. It is a javascript code that uploads images. How would I make it so that every time an image gets uploaded, it gets a new ID so I can drag and drop it without interference.
window.onload = function(){
//Check File API support
if(window.File && window.FileList && window.FileReader)
{
var filesInput = document.getElementById("files");
filesInput.addEventListener("change", function(event){
var files = event.target.files; //FileList object
var output = document.getElementById("result");
for(var i = 0; i< files.length; i++)
{
var file = files[i];
//Only pics
if(!file.type.match('image'))
continue;
var picReader = new FileReader();
picReader.addEventListener("load",function(event){
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img id='thumbnail' draggable='true' ondragstart='drag(event)' src='" + picFile.result + "'" +
"title='" + picFile.name + "'/>";
output.insertBefore(div,null);
});
//Read the image
picReader.readAsDataURL(file);
}
});
}
else
{
console.log("Your browser does not support File API");
}
}
Fiddle to show how it works: http://jsfiddle.net/Yvgc2/1563/
Currently, all the images have the same id when uploaded so drag and drop cannot occur.
Quick and dirty: use a global variable (window.thumbId).
The reason why you shouldn't use the i variable is, that it will restart each time you upload picture(s).
window.thumbId will work regardless how many times and how many images you upload. You'll get ids like thumbnail1, thumbnail2, etc:
window.thumbId = (window.thumbId || 0)+1;
div.innerHTML = "<img id='thumbnail"+window.thumbId+"' draggable='true' ondragstart='drag(event)' src='" + picFile.result + "'" +
"title='" + picFile.name + "'/>";
If the files get stored in a DB, you can use the db index as a unique id, get last index and +1 on each new item.
If not you can use the loops index and replace this line
div.innerHTML = "<img id='"+i+"' draggable='true' ondragstart='drag(event)' src='" + picFile.result + "'" +
"title='" + picFile.name + "'/>";

dynamic id generation using File reader() javascript

i am trying to create multiple image uploader with delete option,till now i am able to select unique multiple files but i want to have a delete option .for that i have to generate an id for each image to delete it before uploading :
window.onload = function(){
//Check File API support
if(window.File && window.FileList && window.FileReader)
{
var filesInput = document.getElementById("files");
filesInput.addEventListener("change", function(event){
var files = event.target.files; //FileList object
var dive = $(".overview").find('img').length;
var output = document.getElementById("result");
// console.log(files);
for(var i = 0; i< files.length; i++)
{
var file = files[i];
//Only pics
if(!file.type.match('image'))
continue;
$(".overview .imgdivcon").each(function(){
var its =$(this).children().eq(1).attr("title");
if(its == file.name ){
throw alert("already exits") ;
}
});
var divn = i+dive+1;
var picReader = new FileReader();
console.log(divn);
picReader.addEventListener("load",function(event){
var picFile = event.target;
var div = document.createElement("div");
div.className="imgdivcon";
div.innerHTML = "<p onclick='sliceimg("+divn+")' class='close' name='"+i+"' id='cl'>x</p><img width='150' height='150' class='thumbnail' src='" + picFile.result + "'" +
"title='" + file.name + "'/>";
output.insertBefore(div,null);
});
//Read the image
picReader.readAsDataURL(file);
}
});
when i'm selecting single image its generating unique id for each image ,but when i'm selecting multiple images it's giving total images count for each image but not a unique count.
here is my js fiddle link http://jsfiddle.net/aerfan/CdgUV/ little help wil be aprreciated .
You got wrong unique id (should be the order of image instead of total count) when you select multiple images because the "divn" variable will be the total count of images when the picReader load event handler being triggered.
Closure will add local variable to its scope when function was created. The outer for loop finished before file reader load callback being executed and divn will be the total count of images.
for(var i = 0; i< files.length; i++)
{
......
var divn = i+dive+1; //this variable will be added to callback closure
.......
picReader.addEventListener("load",function(event){
........
//divn is always the total count of images
div.innerHTML = "<p onclick='sliceimg("+divn+")' class='close' name='"+i+"' id='cl'>x</p><img width='150' height='150' class='thumbnail' src='" + picFile.result + "'" +
"title='" + file.name + "'/>";
output.insertBefore(div,null);
});
}
To solve this problem, you could try to use currying technique. Let's update the picReader load event callback:
picReader.addEventListener("load",(function(divn){
return function(event){
var picFile = event.target;
var div = document.createElement("div");
div.className="imgdivcon";
div.innerHTML = "<p onclick='sliceimg("+divn+")' class='close' name='"+i+"' id='cl'>x</p><img width='150' height='150' class='thumbnail' src='" + picFile.result + "'" +
"title='" + file.name + "'/>";
output.insertBefore(div,null);
};
})(divn));
You can prefill argument (divn) , use closure to remember its status and return new function by using currying.
Hope this is helpful for you.

Categories

Resources