Multiple thumbnail images before upload with FileReader - javascript

I need help with making thumbnail images before uploading. I read a lot of questions here, but my problem (as I think) is a bit different.
So my code:
function nextImg(num, name){
var out = '<div class="row"><div class="col-md-2 col-xs-12" id="img_'+num+'"><img class="img-responsive pad img-thumbnail" id="imgreal_'+num+'" src="#" alt="'+name+'"></div></div>';
return out;
}
function showImgs(event){
var files = event.target.files;
if(files['length'] > 0){
var div_edit = ""
for(var i = 0; i < files['length']; i++){
div_edit += nextImg(i,files[i]['name']);
}
document.getElementById("img_container").innerHTML = div_edit;
for(var i = 0; i < files['length']; i++){
var reader = new FileReader();
console.log(i);
reader.onload = function (e) {
var num = "imgreal_"+i;
console.log(num);
var output = document.getElementById(num);
output.src = reader.result;
}
reader.readAsDataURL(event.target.files[i]);
}
}else{
document.getElementById('mainTAGs').style.display = "none";
}
}
<form role = "form" method = "post">
<div class="container col-md-12 col-xs-12" id="img_container">
</div>
</form>
So when I iterate over the opened images in the second for cycle the code gave all reader.onload function uniq "imgreal_#" id.
When onload was called I log the id and it is always the end criteria of the for cycle.
For example, let say I want to upload 3 images, then all the getElementById in the reader.onload function called with "imgreal_3" instead of "imgreal_0"... "imgreal_2".
Any suggestion how to solve it?
THX in advance! :)

This is the common gotcha for JS. To make it work you should store somewhere current iteration value for the async functions to access it later. E.g. you can store the index in IIFE:
(function() {
reader.onload = function (e) {
var num = "imgreal_"+i;
console.log(num);
var output = document.getElementById(num);
output.src = reader.result;
}
})(i)
The solution was:
(function(n) {
var reader = new FileReader();
reader.onload = function (e) {
var num = "imgreal_"+n;
console.log(num);
var output = document.getElementById(num);
output.src = reader.result;
}
reader.readAsDataURL(event.target.files[n]);
})(i);

Related

Convert each image input to DataUrl or Base64 image while iterating

var toPush = []
for(var i = 1; i <= myVariable; i++){
var variable1 = document.getElementById('q' + i).value;
var var2 = document.getElementById(i + 'x').value;
var var3 = document.getElementById(i + 'y').value;
var var4 = document.getElementById(i + 'z').value;
var var5 = document.getElementById(i + 'd1').value;
var var6 = document.getElementById('xy' + i).value;
var file = document.getElementById("fileup" + i);
var twofour = [var2, var3, var4, var5];
let reader = new FileReader();
reader.readAsDataURL(file.files[0]);
reader.onload = function () {
pictureURL = reader.result;
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
toPush.push({"variable1": variable1, "twofour": twofour, "pictureURL": pictureURL}
}
The application can add X many inputs by appending them do div. When it comes to pushing the data, I want to have the file input, which is the only image input, be read as DataURL, so it can show be used as a source to an image preview. I don't know if it is because of the iteration, but the pictureURL variable pushes as empty: in the database I got "pictureURL": "".
Is there any way around it?
Thank you in advance.
There are multiple issues with this code. The reason that you're not receiving the pictureUrl is because you're reading it before it's ready.
FileReader reads the file asynchronously. It provides us a callback onload that is executed when it has read the file. We get the content of the file as reader.result only when this callback is executed. You have to rewrite your code to process the content of the file when it's either FileReader.onload or FileReader.onerror are executed.
See the working example below. I have removed unnecessary code. You can run the code by clicking on Run code snippet button at the bottom of the post
function showIcons() {
let files = document.querySelector('#files').files;
if(files.length) {
document.querySelector('#show-icons-error').textContent = "";
} else {
document.querySelector('#show-icons-error').textContent = 'No files have been selected';
}
let imageIcons = document.querySelector('#image-icons');
imageIcons.innerHTML = '';
let imageUrlArr = [];
for(var i = 0; i < files.length; i++) {
let imageIconHolder = document.createElement('img');
imageIconHolder.classList.add('image-icon');
imageIconHolder.setAttribute('image-index', i);
imageIcons.appendChild(imageIconHolder);
let file = files[i];
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
pictureURL = reader.result;
imageIconHolder.src = pictureURL;
imageUrlArr[i] = pictureURL;//Setting ith image, not pushing.
};
reader.onerror = function (error) {
console.log(`Error loading image at index : ${i}, error: ${error}`);
imageUrlArr[i] = error;//Error for ith image
};
}
}
.image-icon {
width: 10em;
display: block;
min-height: 5em;
}
<html>
<body>
<label for="files" class="btn">Select Images</label>
<input id="files" type="file" value="Select File" accept="image/*" multiple="multiple">
<br/>
<br/>
<div>
<button id="show-icons" onclick="showIcons()">Show Icons</button>
<span id='show-icons-error' style="color: red; font-weight: bold;"></span>
</div>
<br/>
<div id="image-icons">
</div>
</body>
</html>

Importing data to localstorage

Could anybody help me out sorting the following code or help me in the right direction?
It needs to import data from a .txt file and store it into localstorage as key & value.
Key is before ':' and value comes after it. A new key / value is separated after each ','.
Sample data from .txt file is:
nl-step1chapter1Question6:U2FsdGVkX19bRT84xShxK+29ypgj1d6ZHt+2DVBCUtY=,nl-step1chapter1Question1:U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=,nl-step1chapter1Question4:U2FsdGVkX1+9SVVOvTKeZuaQGj58L5WnEgL8htS0c7U=,jft:320982da-f32a-46a2-a97c-605ebe305518,nl-step1chapter1Question5:U2FsdGVkX19pi8A+PQZ7rBNCWrFeCwl2HdXpV+wWkFk=,nl-step1chapter1Question2:U2FsdGVkX19hnRnpmP3omzYNU0jXd3NtsHM+mvGYBnc=,nl-step1chapter1Question3:U2FsdGVkX1+hPbMRN+x19y7pF73eXoxG0qK1igZYZbA=
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="application/x-javascript">
$(function importData() {
document.getElementById('file').onchange = function () {
//debugger;
var file = this.files[0];
var reader = new FileReader();
reader.onload = function (progressEvent) {
//console.log(this.result.split(','));
var lines = this.result.split(',');
var list = [];
for (var line = 0; line < lines.length; line++) {
list.push(lines[line]);
localStorage.setItem([line],lines);
}
};
reader.readAsText(file);
};
});
</script>
Any help is much appreciated!
The way you are using FileReader doesn't seem correct to me. This is how your importData() function should be:
$(function importData() {
document.getElementById('file').onchange = function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
var text = reader.result;
var lines = text.split(',');
for (var line = 0; line < lines.length; line++) {
let elements = lines[line].split(':');
localStorage.setItem(elements[0], elements[1]);
}
};
reader.readAsText(input.files[0]);
};
});
It will insert the elements in the localStorage as you described. For example: key = step1chapter1Question1 and value = U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=

easier way to code series of images javascript

In the code below, I've successfully made it so when someone clicks on a thumbnail of a picture, it's full picture will come up in the target.
I was wondering if anyone knew of a nicer way to create onclick events for multiple thumbnails I would like to make this expandable.
Also, please consider only javaScript answers as I don't write in jQuery (yet!).
var img = document.getElementById("galleryImg");
var thumb = document.getElementsByClassName("imgThumb");
var p0 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic1.png";
var p1 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic2.png";
var p2 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic3.png";
var p3 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic4.png";
var p4 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic5.png";
var p5 = "https://dl.dropboxusercontent.com/u/89495286/test/images/pic6.png";
var pArray = [p0, p1, p2, p3, p4, p5]
img.src = pArray[0];
thumb[0].onclick = function(){pic0()};
function pic0(){img.src = pArray[0]};
thumb[1].onclick = function(){pic1()};
function pic1(){img.src = pArray[1]};
thumb[2].onclick = function(){pic2()};
function pic2(){img.src = pArray[2]};
thumb[3].onclick = function(){pic3()};
function pic3(){img.src = pArray[3]};
thumb[4].onclick = function(){pic4()};
function pic4(){img.src = pArray[4]};
thumb[5].onclick = function(){pic5()};
function pic5(){img.src = pArray[5]};
Assuming that you'll have the same amount of elements in pArray as you would have thumbnails, you would do this:
Array.prototype.forEach.call(thumb, function(thumbItem, index) {
thumbItem.onclick = function() {
img.src = pArray[index];
};
});
It iterates through the thumbnails, attaching the onclick handler and assigning the main image with the corresponding image based on the element's index.
You could use the loop for any array, we usually use the for loop for related arrays.
for (var i = 0; i < thumb.length; i++) {
thumb[i].src = pArray[i]; // change if different src
thumb[i].onclick = function() { img.src = this.src; };
}
Try on: https://jsfiddle.net/Zay_DEV/8r1gqnfx/
var img = document.getElementById("galleryImg");
var thumb = document.getElementsByClassName("imgThumb");
var pArray = [
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic1.png",
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic2.png",
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic3.png",
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic4.png",
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic5.png",
"https://dl.dropboxusercontent.com/u/89495286/test/images/pic6.png"
]
for (var i = 0; i < thumb.length; i++) {
thumb[i].src = pArray[i];
thumb[i].onclick = function() { img.src = this.src; };
}
thumb[0].onclick();
#galleryImg { max-height: 25vh; }
.imgThumb { width: 100px; height: 50px; }
<img id="galleryImg"/>
<br />
<img class="imgThumb"/>
<img class="imgThumb"/>
<img class="imgThumb"/>
<img class="imgThumb"/>
<img class="imgThumb"/>
<img class="imgThumb"/>
You iterate over the array, however, using a loop alone will not completely fix the problem, because you're assigning a function inside the loop, which means any outside variables could change before the onclick executes.
So what you need is a closure.
Create a function that sets the onclick callback
Call that function in a loop.
Here's an example:
function setOnClick(element, imgSrc) {
element.onclick = function() { img.src = imgSrc; };
}
for (var i = 0; i < thumb.length; i++) {
setOnClick(thumb[i], pArray[i]);
}
This functionality is very common, so it has also been implemented by Array#forEach:
pArray.forEach(function(imgSrc, index) {
thumb[index].onclick = function() { img.src = imgSrc; };
});

javascript for-loop repeating last iteration

I am trying to crop-resize n display an image on client-browser using JS. I am able to do so except that my text loop is wrong. You can see in the image below that it is repeating the last iteration. My image loop works fine though. Hint - First filename text is supposed to be black.jpg.
Am unable to solve the issue after having tried for several hours. Given below is a trimmed version of the code. If needed here is the complete version of the script. Please help, I am still learning.
HTML
<input type="file" id="input" onchange="prevCrop()" multiple>
<ul id="listWrapper"></ul>
JAVASCRIPT
function prevCrop() {
var files = document.querySelector('input[type=file]').files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var fileSize = Math.floor(file.size/1024);
var info = file.name + " " + fileSize + " KB : iteration number - " + i;
var reader = new FileReader();
if (reader != null) {
reader.onload = GetThumbnail;
reader.readAsDataURL(file);
}
}
function GetThumbnail(e) {
var canvas = document.createElement("canvas");
var newImage = new Image();
newImage.src = e.target.result;
newImage.onload = cropResize;
function cropResize() {
canvas.width = 70;
canvas.height = 70;
### more codes here that handles image ###
var dataURL = canvas.toDataURL("image/jpg");
var thumbList = document.createElement('div');
thumbList.setAttribute('class', 'tlistClass');
var nImg = document.createElement('img');
nImg.src = dataURL;
var infoSpan = document.createElement('span');
infoSpan.innerHTML = info;
var handle = document.getElementById("listWrapper").appendChild(thumbList);
handle.appendChild(nImg);
handle.appendChild(infoSpan);
}
}
}
This happens because the onload callback function is triggered asynchronously, so after your code has already ended. At that time info has the string that was assigned to it in the last iteration. Only then the onload events are fired, resulting in the different calls of GetThumbnail, which all will see the same value for info.
Instead bind the value of info to the callback function like this:
reader.onload = GetThumbnail.bind(null, info);
...and define the corresponding parameter for it in the GetThumbnail function:
function GetThumbnail(info, e) {
That way every call of that function will posses of the proper value for info.

Variable isn't increementing on file reader

I'm trying to do thumbnails for the images uploaded. I want to create diferents classes for the thumbs, for this, I'm trying to use de i of the loop. But this assume just the final value. Why? Thanks
for (var i = 0; i < inp.files.length; i++) {
//create the thumbnails
var reader = new FileReader();
reader.onload = function (e) {
$('.thumbs').append('<div class="thumb_item" id="c'+ i +'"></div>');
$('<img />').attr({'src': e.target.result}).addClass('img_thumb').appendTo('.thumb_item:last');
};
reader.readAsDataURL(this.files[i]);
}
Why can't you apply the same technique pointed out in the comment?
reader.onload = (function(index)
{
return function (e)
{
$('.thumbs').append('<div class="thumb_item" id="c'+ index +'"></div>');
$('<img />').attr({'src': e.target.result}).addClass('img_thumb').appendTo('.thumb_item:last');
};
})(i);
Thanks for all, I solved this way:
for (var i = 0; i < inp.files.length; i++) {
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function (theFile) {
var count = i;
return function (e) {
$('.thumbs').append('<div class="thumb_item" id="c'+ count +'"></div>');
$('<img />').attr({'src': e.target.result}).addClass('img_thumb').appendTo('.thumb_item:last');
};
})(i);
reader.readAsDataURL(this.files[i]);
}

Categories

Resources