jQuery image preview for multiple image upload - javascript

I am using this code in my image uploader:
Html:
<input type="file" id="files" name="files[]" multiple />
<ul id="list"></ul>
JS:
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var li = document.createElement('li');
li.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/><input type="text" class="rename" name="',i,'" value="',escape(theFile.name),'"><input type="text" class="reorder" name="',i,'" value="',i,'">'].join('');
document.getElementById('list').insertBefore(li, null);
};
})(f);
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
My problem is when it gets to reader.onload, the for loop is done so i = #of images not image # like I want it to be. Is there any way to fix this so the name of my inputs is the image # of the image they're next to?

reader.onload runs as many times as there are pictures being uploaded.
var x = 0;
reader.onload = (function(theFile) {
return function(e) {
console.log(x);
x++;
};
})(f);

You already have a solution for f. You could just do exactly the same thing for i: pass it to the handler's closure:
reader.onload = (function(theFile, i) {
return function(e) {
var li = document.createElement('li');
li.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/><input type="text" class="rename" name="',i,'" value="',escape(theFile.name),'"><input type="text" class="reorder" name="',i,'" value="',i,'">'].join('');
document.getElementById('list').insertBefore(li, null);
};
})(f, i);

Related

Javascript getElementById but keep old value

I have a file_field_tag inside a rails form with a select file field:
<%= file_field_tag "attachments[media_files][]", multiple: true, id: "files" %>
And I have an area to preview the images/videos and remove if need be:
<span id="result"></span>
Everything is working correct but there is only one glitch.... If the images/videos are in separate folders, I have to add the files from one folder first and then from the other folder. The files show at the preview, but after this process only the second batch of files gets saved when I submit the form.
Here is the javascript for all of the above:
window.onload = function(){
if(window.File && window.FileList && window.FileReader)
{
var filesInput = document.getElementById("files");
filesInput.addEventListener("change", function(event){
var files = event.target.files;
var output = document.getElementById("result");
for(var i = 0; i< files.length; i++)
{
var file = files[i];
if (!file.type.match(/.(jpg|jpeg|png|gif|mp4|avi|flv|wmv|mov|tiff|bmp|exif)$/i))
continue;
var picReader = new FileReader();
picReader.addEventListener("load",function(event){
var picFile = event.target;
var span = document.createElement("span");
span.innerHTML = ['<img class="thumb" src="', picFile.result, '" title="', picFile.name, '"/><span class="remove_img_preview"></span>'].join('');
output.insertBefore(span,null);
span.children[1].addEventListener("click", function(event){
span.parentNode.removeChild(span);
});
});
picReader.readAsDataURL(file);
}
});
}
else
{
console.log("Your browser does not support File API");
}
}
Depending on what you're trying to achieve you could just save the files into an array variable:
////////////////////////////////
// Create an array for the files somewhere
var fileCache = [];
////////////////////////////////
window.onload = function(){
if(window.File && window.FileList && window.FileReader)
{
var filesInput = document.getElementById("files");
filesInput.addEventListener("change", function(event){
var files = event.target.files;
var output = document.getElementById("result");
for(var i = 0; i< files.length; i++)
{
var file = files[i];
if (!file.type.match(/.(jpg|jpeg|png|gif|mp4|avi|flv|wmv|mov|tiff|bmp|exif)$/i))
continue;
////////////////////////////////
// Add each file to the array as you process it
fileCache.push(file);
////////////////////////////////
var picReader = new FileReader();
picReader.addEventListener("load",function(event){
var picFile = event.target;
var span = document.createElement("span");
span.innerHTML = ['<img class="thumb" src="', picFile.result, '" title="', picFile.name, '"/><span class="remove_img_preview"></span>'].join('');
output.insertBefore(span,null);
span.children[1].addEventListener("click", function(event){
span.parentNode.removeChild(span);
});
});
picReader.readAsDataURL(file);
}
});
}
else
{
console.log("Your browser does not support File API");
}
}
<input type="file" id="files" />
Generally, I would avoid using a random global variable, but without knowing exactly what you want to do with the list of files this is the most generic answer.

How to read(image data / dimension / filesize / name) multiple images on file select?

I am getting the same image multiple times.
document.getElementById('getFile').addEventListener('change', getFiles);
function getFiles(ev){
var file= ev.target.files;
if (file) {
for(var i=0,f; f = file[i]; i++){
if(!f.type.match('image.*')){continue;}
var reader = new FileReader();
reader.readAsDataURL(file[i]);
reader.onload = function () {
var img = new Image();
img.onload = function() {
var span = document.createElement('span');
span.innerHTML = ['<img width="100" id="thumb" src="', img.src,'"/>'].join('');
document.body.appendChild(span, null);
var l = document.createElement('span');
l.innerHTML=img.width+'x'+img.height;
span.appendChild(l);
span.style="border:1px solid black; border-radius:5px; display:block";
};
img.src = reader.result;
};
}
}
}
<input id="getFile" type="file" multiple accept="image/jpeg">
I want a list of images with a dimension and image name label.
This is logical, because your onload = async. Therefor the 2nd time the for loop passes the reader reference is updated to the next reader. So the reader.result references to the last reader created and therefor only that result will be shown. Most likely you only get the latest image visible on your screen. To solve this you can use an anonymous function to create a new scope. For example (working fiddle: https://jsfiddle.net/r9ecbLy5/):
document.getElementById('getFile').addEventListener('change', getFiles);
function getFiles(ev) {
var file = ev.target.files;
if (file) {
for (var i = 0, f; f = file[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
// Create a scope where file and reader will not be overwritten by the next loop
(function(fileToUse){
var reader = new FileReader();
reader.readAsDataURL(fileToUse);
reader.onload = function() {
var img = new Image();
img.onload = function() {
var span = document.createElement('span');
span.innerHTML = ['<img width="100" id="thumb" src="', img.src, '"/>'].join('');
document.body.appendChild(span, null);
var l = document.createElement('span');
l.innerHTML = img.width + 'x' + img.height;
span.appendChild(l);
span.style = "border:1px solid black; border-radius:5px; display:block";
};
img.src = reader.result;
// This reader result, was always the last one.
// Because reader was overwritten.
};
})(file[i]);
}
}
}
using function
escape();
I am now able to get the targeted file name information.

How to load image from directory, then show onclick

I am trying to have a function which has functions that do the following.
One function to store the files i get with input into the parent functions loadedimages array(loadimages).
One function to show those files in the correct component(showLoadedImages).
And one function to make the correct img file appear on the correct component.
The last function is what i want it to be like(it does not work).
The other two seem ok.
The problem i have is how to make the last function work while using the loadedimages array. You can change what i store in the array , i wouldnt mind.
Here is the JS code:
function imgviewer() {
"use strict";
var loadedimages = [];
var lidivs = [];
function loadimages() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (!file.name.match(/\.(jpg|jpeg|png|gif)$/)) {
alert('THERE IS NO IMAGE IN THIS DIRECTORY.');
break;
}
loadedimages.push(file);
}
}
function showLoadedImages(elem) {
loadimages();
var ld = loadedimages;
//var files = getLoadedImages(); //filelist obj
for (var i = 0; i < ld.length; i++) {
var file = ld[i];
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement(
'span');
span.innerHTML = [
'<img class="tile" src="',
e.target.result,
'" title="', encodeURI(
file.name), '">'
].join('');
document.getElementById(elem).insertBefore(
span, null);
lidivs.push(span);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
function showImage(index, elem) {
var chosenFile = loadedimages[index];
document.getElementById(elem).src = chosenFile;
}
document.getElementById('images').addEventListener('change', function(){
showLoadedImages("main");
}, false);
}
And some HTML
<form name="uploadForm">
<input id="images" type="file" webkitdirectory mozdirectory directory name="myFiles"
multiple/>
<span id="list"></span>
</form>
<div id="sidebar1"><img id="willchange" src="images/railaythailand.jpg" width="1200" height="832" alt=""/></div>
<div id="main"></div>
When i call showLoadedImages("main") the images are shown in main div. I want to be able to click those images so that they appear on "willchange" .
This does what you asked for. There are a number of other issues with your code that you might want to address, starting with the images are not thumbnails at all (and shrunken images take just as long to load as the original), but perhaps I'm missing something.
"use strict";
var loadedimages = [];
var lidivs = [];
function loadimages() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (!file.name.match(/\.(jpg|jpeg|png|gif)$/)) {
continue;
}
loadedimages.push(file);
}
loadedimages.length || alert('THERE IS NO IMAGE IN THIS DIRECTORY.');
}
function showLoadedImages(elem) {
loadimages();
var ld = loadedimages;
//var files = getLoadedImages(); //filelist obj
for (var i = 0; i < ld.length; i++) {
var file = ld[i];
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement(
'span');
span.innerHTML = [
'<img data-index="',
lidivs.length,
'" class="tile" src="',
e.target.result,
'" title="', encodeURI(
file.name), '">'
].join('');
span.addEventListener("click", foo );
document.getElementById(elem).insertBefore(
span, null);
lidivs.push(span);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
function showImage(index, elem) {
document.getElementById(elem).src = lidivs[index].children[0].src;
}
function foo(event) {
showImage(event.target.dataset.index, "willchange");
}
function show() {
showLoadedImages("list");
}
function init() {
document.getElementById("images").addEventListener("change", show, false);
}
document.addEventListener( "DOMContentLoaded", init, false );
<body>
<form name="uploadForm">
<input id="images" type="file" webkitdirectory mozdirectory directory name="myFiles"
multiple/>
<span id="list"></span>
</form>
<div id="sidebar1"><img id="willchange" src="images/railaythailand.jpg" width="1200" height="832" alt=""/></div>
</body>

How can I display more than one image after uploading several at a time? JS

I'm working on an image organizer that i want to put through google image descriptions. At the moment I'm trying to upload all images at once to the server and display them, for some reason it's only displaying the last choice.
HTML
<input type='file' accept='image/*' multiple onchange="readURL(this);"/>
<div id="images-container"></div>
<label><strong>(Image will display above)</strong></label>
CSS
#blah {
display: block;
}
img {
width:100%;
}
.picContainer {
border: 1px dashed black;
margin: 10px;
padding: 10px;
width: 100px;
display: inline-block
}
JS
$("input").change(function(e) {
for (var i = 0; i < e.originalEvent.srcElement.files.length; i++) {
var file = e.originalEvent.srcElement.files[i];
var div = document.createElement("div");
var img = document.createElement("img");
div.className = "picContainer";
var reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file);
div.appendChild(img);
//$("input").after(div);
var container = document.getElementById("images-container");
container.appendChild(div);
} });
This heppens becouse of variable hoisting in javascript
The compiler rewrites your code so it will look like that:
$("input").change(function(e) {
var i, file, div, img, container;
for (i = 0; i < e.originalEvent.srcElement.files.length; i++) {
file = e.originalEvent.srcElement.files[i];
div = document.createElement("div");
img = document.createElement("img");
div.className = "picContainer";
reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file);
div.appendChild(img);
//$("input").after(div);
container = document.getElementById("images-container");
container.appendChild(div);
} });
what that means for you, is that in the function
reader.onloadend = function() {
img.src = reader.result;
}
the reader and the img variable always equal to the last one (even in the one that was initialized first)
You should probably use a function creator defined before the loop like that:
$("input").change(function(e) {
function initOnReaderLoaded(img, reader){
return function(){
img.src = reader.result;
}
}
for (var i = 0; i < e.originalEvent.srcElement.files.length; i++) {
var file = e.originalEvent.srcElement.files[i];
var div = document.createElement("div");
var img = document.createElement("img");
div.className = "picContainer";
var reader = new FileReader();
reader.onloadend = initOnReaderLoaded(img, reader);
reader.readAsDataURL(file);
div.appendChild(img);
//$("input").after(div);
var container = document.getElementById("images-container");
container.appendChild(div);
} });
You have to create your img element inside the reader onload.
fiddle here:
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
Fiddle

HTML5 multiple images selection limit and edit

I am trying to get a multiple image previewer where the limit is 5 selections, and if I want to change one of my selections, I click the x on that image and re-select a new image
My code right now shows an alert but still allows the selection afterwards. Please help!
DEMO
<input type="file" id="files" name="image_file_arr[]" multiple>
<br><output id="list"></output>
script:
$(function() {
$("input#files[type='file']").change(function(){
var $fileUpload = $("input#files[type='file']");
if (parseInt($fileUpload.get(0).files.length)>6){
alert("You can only upload a maximum of 5 files");
return false;
}
});
});
function handleFileSelect(evt) {
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) { continue; }
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
See this working fiddle http://jsfiddle.net/3D6pd/1/
Just put
var $fileUpload = $("input#files[type='file']");
if (parseInt($fileUpload.get(0).files.length)>6){
alert("You can only upload a maximum of 5 files");
return false;
}
this code inside the handleFileSelect function and use evt.stopPropagation();
this is updated fiddle try http://jsfiddle.net/3D6pd/6/
I have added a form and a submit button here,
$("#sub").click(function(e){
e.preventDefault();
alert("files uploaded="+ $("input#files[type='file']").get(0).files.length);
});
var count=0;
hope this helps
I have added this to your fiddle
$(function() {
var array=[];
$("#sub").click(function(e){
e.preventDefault();
$("input#files[type='file']").get(0).files=array;
alert("files uploaded="+ $("input#files[type='file']").get(0).files.length);
});
var count=0;
// Preview should only have up to 5 images
// onclick x, preview of that image should be deselected and removed from the preview screen
function handleFileSelect(evt) {
var $fileUpload = $("input#files[type='file']");
count=count+parseInt($fileUpload.get(0).files.length);
if (parseInt($fileUpload.get(0).files.length)>6 || count>5){
alert("You can only upload a maximum of 5 files");
count=count-parseInt($fileUpload.get(0).files.length);
evt.preventDefault();
evt.stopPropagation();
return false;
}
var files = evt.target.files;
array = array.concat(files);
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) { continue; }
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/><span class="remove_img_preview">x</span>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
});

Categories

Resources