Using readAsDataURL() for image preview - javascript

jsFiddle URL: http://jsfiddle.net/Xotic750/AjtLx
Been working on this all day and I can't see the issue. It's probably due to my narrow understanding of how FileReader objects work but what I'm trying to do is use readAsDataURL() to obtain images a user has selected and preview them on the screen in a table. Everything is working minus...you guessed it...the preview...well sort of. I'm thinking I'm close because the preview will work, BUT it only displays the last image of the set. Say for example if I uploaded 6 images then the first row of 3 images would be broken, the second row the first 2 would be broken, then the final 6th image would display the preview....Any advice greatly appreciated. Also, once this works it might help others trying to do the same thing because I've searched all over for a solution to this issue and I can't seem to dig anything up....
function PreviewImages() {
var inputID = document.getElementById('input_clone');
var totalImages = inputID.files.length;
var imagesPerRow = 3;
var numRows = totalImages / imagesPerRow;
var row = "";
var cell = "";
var element1 = "";
var elementID = "";
for(var i = 0; i < numRows; i++){ //create rows
row = document.getElementById('image_preview_table').insertRow(i);
for(var ii = 0; ii < imagesPerRow; ii++){ //create cells
cell = row.insertCell(ii);
elementID = "img_" + ii;
element1 = document.createElement("img");
element1.name = elementID;
element1.id = elementID
cell.appendChild(element1);
oFReader = new FileReader();
oFReader.onload = function(oFREvent){
var dataURI = oFREvent.target.result;
var image = document.getElementById(elementID);
image.src = dataURI;
};
oFReader.readAsDataURL(document.getElementById("input_clone").files[ii]);
}
}
}

The problem with your code is: readAsDataURL() is asynchronous, you should wait until it finishes reading before you invoke a second reading or make a new instance by calling new FileReader().
The answer by Xotic750 works because he creates one FileReader for each image, while you used only one FileReader.
But, using FileReader to preview images is not a good choice, as FileReader.readAsDataURL() converts the whole image to a large string (in the form of "data:image/jpeg;base64,/9j/4SVaRXhpZgAAS......"), and you show the image by placing the whole string of image data into the img.src attribute, if your image is large, you take the risk of running out of memory.
img.src is meant to contain the url of the image, not the data of the image, although you can assign a url containing the whole image data via img.src = "data:image/jpeg;......".
So, you should use window.URL.createObjectURL() to create a url referring to your local image, and assign this url to img.src:
...
img.src = window.URL.createObjectURL(fileInput.files[i]);
...

Here is a solution, quite a bit of adjustment was made to your original to get it to work, as you will probably notice.
CSS
div.rounded {
width: 100%;
border-style: solid;
border-width: 1px;
border-radius: 5px;
}
label {
display: block;
}
input {
display: block;
}
#previewTable {
width: 100%;
}
HTML
<div id="imagesDiv" class="rounded">
<label for="chooseFiles">Add Images</label>
<input type="file" id="chooseFiles" multiple="multiple" />
<table id="previewTable">
<thead id="columns"></thead>
<tbody id="previews"></tbody>
</table>
</div>
Javascript
(function (global) {
var imagesPerRow = 3,
chooseFiles,
columns,
previews;
function PreviewImages() {
var row;
Array.prototype.forEach.call(chooseFiles.files, function (file, index) {
var cindex = index % imagesPerRow,
oFReader = new FileReader(),
cell,
image;
if (cindex === 0) {
row = previews.insertRow(Math.ceil(index / imagesPerRow));
}
image = document.createElement("img");
image.id = "img_" + index;
image.style.width = "100%";
image.style.height = "auto";
cell = row.insertCell(cindex);
cell.appendChild(image);
oFReader.addEventListener("load", function assignImageSrc(evt) {
image.src = evt.target.result;
this.removeEventListener("load", assignImageSrc);
}, false);
oFReader.readAsDataURL(file);
});
}
global.addEventListener("load", function windowLoadHandler() {
global.removeEventListener("load", windowLoadHandler);
chooseFiles = document.getElementById("chooseFiles");
columns = document.getElementById("columns");
previews = document.getElementById("previews");
var row = columns.insertRow(-1),
header,
i;
for (i = 0; i < imagesPerRow; i += 1) {
header = row.insertCell(-1);
header.style.width = (100 / imagesPerRow) + "%";
}
chooseFiles.addEventListener("change", PreviewImages, false);
}, false);
}(window));
On jsfiddle

Related

Looping over <input multiple> with FileReader only displays last image in grid of divs

I am trying to create a HTML form that allows the user to upload up to 5 images. After the user selects the images (from the filesystem using <input multiple>) I'd like to display them in 5 divs (ID'ed from #contactImage0 to #contactImage4).
To achieve this I loop over the selected images using vanilla JS and set them as background-image in the corresponding div. The problem is that only the last image in a selection actually shows up. All other divs stay empty.
After debugging a bit I found out that for all but the last image reader.result returns "" but I can't figure out why as the "image" variable I pass to reader.readAsDataURL() seems to be valid.
Why is that and how do I fix it?
HTML:
<div id="contactImage0" class="imagePreview"></div>
<div id="contactImage1" class="imagePreview"></div>
<div id="contactImage2" class="imagePreview"></div>
<div id="contactImage3" class="imagePreview"></div>
<div id="contactImage4" class="imagePreview"></div>
<input id="upload" type="file" onchange="LoadImage(this)" name="image" accept="image/*" multiple>
JavaScript:
function LoadImage(input){
if (input.files.length > 5){
alert("You may only select a maximum of 5 images!");
// TODO: Remove selected images
return;
}
var i;
for (i = 0; i < input.files.length; i++){
var image = input.files[i]
var imageDiv = 'contactImage' + i.toString();
var element = document.getElementById(imageDiv);
var reader = new FileReader();
reader.onloadend = function(){
element.style.backgroundImage = "url(" + reader.result + ")"; // <-- RETURNS EMPTY STRING (EXCEPT FOR LAST IMAGE)
}
if(image){
reader.readAsDataURL(image);
}else{
}
}
}
CSS (Shouldn't be the cause of the problem but you never know):
.imagePreview{
margin: .5em;
display: inline-block;
width: calc(100% - 1em);
height: 80px;
background-color: #fff;
background-image:url('');
background-size:cover;
background-position: center;
}
See https://dzone.com/articles/why-does-javascript-loop-only-use-last-value
Solution 1 - Creating a new function setElementBackground
for (i = 0; i < input.files.length; i++) {
setElementBackground(input.files[i], i);
}
function setElementBackground(file, i) {
var image = file;
var imageDiv = "contactImage" + i.toString();
var element = document.getElementById(imageDiv);
var reader = new FileReader();
reader.onloadend = function() {
element.style.backgroundImage = "url(" + reader.result + ")";
};
if (image) {
reader.readAsDataURL(image);
}
}
Solution 2 - Using Another Closure inside for loop
for (i = 0; i < input.files.length; i++) {
(function() {
var count = i;
var image = input.files[count];
var imageDiv = "contactImage" + count.toString();
var element = document.getElementById(imageDiv);
var reader = new FileReader();
reader.onloadend = function() {
element.style.backgroundImage = "url(" + reader.result + ")";
};
if (image) {
reader.readAsDataURL(image);
}
})();
}

Javascript Image change onClick

I'm creating an image that changes on click. My code isn't working whats wrong with it?
<div id="img"></div>
<script>
var fNames = ["SD1", "SD2", "SD3", "SD4"]; //File names
var _img = document.getElementById("img"); //Grabs images, groups them
var imgIdx = 0;
_img.style.position = "relative";
_img.style.left = "auto";
_img.style.right = "auto";
_img.style.width = "1920";
_img.style.height = "1280";
_img.style.backgroundImage = "url('images/"+fNames[imgIdx]+".jpg')"; //Retrieves images from file
_img.addEventListener("click", onImageClick); //Allows image click
function onImageClick() {
imgIdx++;
if(imgIdx == 6) {
imgIdx = 0;
}
_img.style.backgroundImage = "url('images/"+fNames[imgIdx]+".jpg')";
}
</script>
You need a unit when you specify the size:
_img.style.width = "1920px";
_img.style.height = "1280px";
When making the index wrap around you are using 6, but it should be 5. Better yet, use the length of the array, that way you don't need to change that part of the code if the array changes:
if(imgIdx > fNames.length) {
imgIdx = 0;
}

How can I display the image name on mouseover?

I have 2 arrays, an image array and a price array.
I have the images displaying, but I'd like to display the price, which at the moment I have held in the image name, when I mouseover the image, is it possible?
HTML
<section id=main>
<!--populate with images from array-->
<p id="photos" class="product_display">
<script>getImage();</script>
</section>
JS
//image array for products
var imageArray = new Array();
imageArray[0]="images/coffee_prod_1.png";
imageArray[1]="images/coffee_prod_2.png";
imageArray[2]="images/coffee_prod_3.png";
imageArray[3]="images/coffee_prod_4.png";
imageArray[4]="images/coffee_prod_5.png";
imageArray[5]="images/coffee_prod_6.png";
imageArray[6]="images/coffee_prod_7.png";
//price array for products
var priceArray = ["€11.90", "€12.90", "€13.90", "€14.90", "€15.90", "€16.90", "€17.90"];
function getImage(){
var container = document.getElementById("photos");
for (var i=0; i < imageArray.length; ++i) {
var img = new Image();
img.src = imageArray[i];
img.className = "product_details";
img.name = priceArray[i];
container.appendChild(img);
}
}
I thought I might be able to add something like 'img.onmouseover = imageMouseOver(priceArray[i]);' to my getImage function, and then have something inside the function that would display the image name (ideally over the image) on mouseover. I was hoping to apply an opaque colour too so the image name might be clearer.
Any help would be appreciated!
I would suggest creating a container div for each photo, and in each container add your and like another div that contains the price. Set the price divs hidden at first with display:none or something, and then in your javascript add some listeners to show and hide it:
for (var i=0; i < imageArray.length; ++i) {
var img = new Image();
img.src = imageArray[i];
img.className = "product_details";
var container = document.createElement("div");
var price = document.createElement("div");
price.innerHTML = priceArray[i];
//Style your price and image elements so they fit in the container and the price is displayed over the image etc
container.appendChild(price);
container.appendChild(img);
container.onmouseover = function() {
container.getElementByClass("price").style.display = "block";
};
container.onmouseout = function() {
container.getElementByClass("price").style.display = "none";
};
}
You may need to fiddle with the onmouseover/out events a bit if showing the price div messes it up i.e. onmouseover is called on the layer with the highest z index instead of container, I haven't tried it but this should give you a rough idea
Working example here:
http://jsfiddle.net/0vts5291/
HTML
<section id=main>
<!--populate with images from array-->
<p id="photos" class="product_display">
</section>
JS
var imageArray = new Array();
imageArray[0]="images/coffee_prod_1.png";
imageArray[1]="images/coffee_prod_2.png";
imageArray[2]="images/coffee_prod_3.png";
imageArray[3]="images/coffee_prod_4.png";
imageArray[4]="images/coffee_prod_5.png";
imageArray[5]="images/coffee_prod_6.png";
imageArray[6]="images/coffee_prod_7.png";
//price array for products
var priceArray = ["€11.90", "€12.90", "€13.90", "€14.90", "€15.90", "€16.90", "€17.90"];
function getImage() {
var container = document.getElementById("photos");
for (var i = 0; i < imageArray.length; ++i) {
var img = new Image();
img.src = imageArray[i];
img.className = "product_details";
img.name = priceArray[i];
img.title = priceArray[i];
container.appendChild(img);
}
}
getImage();

How to replace a table cell value with a picture?

I've got a table output that is generated dynamically.
I've got a javascript function that is doing some coloring and replacement of cell values, which is fine (e.g. background becomes green when innerText==3; please find the sample code below).
Now I'm trying to insert a picture in those table cells that have innerText==9.
Is it possible to include this functionality in my function?
I tried with a pictureGrey declaration an assigned it to innerText, but this doesn't work.
Does anyone have an idea, can someone help me?
Thanks a lot in advance,
Gero B.
function ModifyData( ) {
var Elements=document.getElementsByTagName('td');
pictureGrey="Z_led_grey.gif";
for(var i=0;i<Elements.length;i++) {
if(Elements[i].innerText=='3') {
Elements[i].style.color='green';
Elements[i].style.background='green';
Elements[i].innerText=' ';
}
if(Elements[i].innerText=='9') {
Elements[i].innerText= pictureGrey ;
}
}
}
please check the below code.
function ModifyData( ) {
var Elements=document.getElementsByTagName('td');
pictureGrey="Z_led_grey.gif";
for(var i=0;i<Elements.length;i++) {
if(Elements[i].innerText=='3') {
Elements[i].style.color='green';
Elements[i].style.background='green';
Elements[i].innerText=' ';
}
if(Elements[i].innerText=='9') {
Elements[i].innerText=' ';
var img = document.createElement("img");
img.src=pictureGrey;
Elements[i].appendChild (img );}
}
}
demo link:
http://jsfiddle.net/asimshahiddIT/acz5xn3b/
You could create a new image element and append it in the element. Depending on if the data can subsequently change again you might need to check for the existence of the image first.
CSS
img.myFullSizeImageClass {
width: 100%;
height: 100%;
}
JavaScript
function ModifyData() {
var Elements = document.getElementsByTagName('td');
pictureGrey = "angular-icon.png";
for (var i = 0; i < Elements.length; i++) {
if (Elements[i].firstChild.nodeValue == '3') {
Elements[i].style.color = 'green';
Elements[i].style.background = 'green';
Elements[i].firstChild.nodeValue = ' ';
}
else if (Elements[i].firstChild.nodeValue == '9') {
Elements[i].firstChild.nodeValue = "";
var img = document.createElement("img");
img.src = pictureGrey;
img.className = 'myFullSizeImageClass'
Elements[i].appendChild(img);
}
}
}

load an image in a particular element using javascript

I have a place for the main photo (id = "main") and other photos in the table (id = "pic+1(2,3,4,5,6)"). I would like to click on a photo with id = "pic n" and to load it in the element with id "main". How to realize this?
Also I have an error
Uncaught TypeError: Cannot set property 'onclick' of null.
Here is my code:
<img id="main" src="">
galery(dir){
...
...
function createPreview() {
var f = document.createElement("table");
var row = f.insertRow();
for(var j = 0; j < count ; j++) {
var cell = row.insertCell(j);
var img = new Image();
img.src = dir + '/' + j + ".jpg";
img.width = "100";
img.id = 'pic' + j;
cell.appendChild(img);
}
document.body.appendChild(f);
}
document.getElementById(this.pic).onclick = function() {
document.getElementById('main').src = this.src;
}
...
}
Although your code has many errors and misunderstandings on how js works here is some simple code that solves this issue
//fake an image directory
var count = 3;
var dir = 'http://lorempixel.com/300/300/abstract/'
//setup a table
var f = document.createElement("table");
var row = f.insertRow();
for (var j = 0; j < count; j++) {
var cell = row.insertCell(j);
imagepath = dir + j;
cell.innerHTML = '<img src="' + imagepath + '" width="100" onclick="showmain(' + j + ')">';
}
document.body.appendChild(f);
//change imagesrc in main tag
function showmain(image) {
document.getElementById('main').src = dir + image;
}
This is the most basic (noob) thing you do with javascript.
I strongly suggest you read some more tutorials before coming back with such basic questions.
Here is a Plunker.
No onload handler, so the script is at the end of the page.
You have a lot of errors in your code:
1) You define var f inside the function, so its scope is inside the function. But then you try to get it outside the function with document.body.appendChild(f); which doesn't work. you should move document.body.appendChild(f); inside the function.
2) Then you get Uncaught TypeError: Cannot set property 'onclick' of null. because document.getElementById(this.pic) returns null. this.pic returns no id.
3) close your img tag.
4) You have 2 extra }
And I am sure there are more if you show us all your code.

Categories