I am working with javascript. I have append html like
<input type="file" class="browse-02 validate[required]" onchange="showMyImage1(this,'thumbnil4')" name="data[question1][question_image4]" id="" />
<img id="thumbnil4" src="images/no_status_image_sp.jpg" class="uploadimg" alt="">
This html comes dynamically when click on add more button.
I have written js as shown in below :
function showMyImage1(fileInput,id) {
//console.log(fileInput);
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
//alert(id);
var img = document.getElementById(id);//$("#"+id)[0];
//alert(img);
console.log(img);
img.file = file;
var reader = new FileReader();
reader.onload = (function(aImg) {
//alert(aImg);
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
Here, this js does not work for appended data. It gives an error at img.file = file; line as img is null.
So what javascript should I have to write?
UPDATE
I modified your code a little bit, the problem why it is not working because the elements are created dynamically in DOM. So normal event listener will not trigger.
To listen events from dynamically created elements, use Jquery on API.
$(document).on('change','.browse-btn', function(){
var thumbnailId = $(this).prev().attr('id');
showMyImage1(this, thumbnailId)
});
$(document).ready(function() {
var n = 5;
var i = 2;
var j = 10;
var k = 2;
$('.addmore').click(function(e){
e.preventDefault();
var div = $('#appendData');
var html = '';
html += '';
html += '<div class="formPartd">';
html += '<div class="formPartd_left">';
html += '<div class="upload-img">';
html += '<label>Upload Image'+n+'</label>';
html += '<span>';
html += '<img id="thumbnil'+j+'" src="" class="uploadimg" alt="">';
html += '<input type="file" class="browse-btn" name="data[question1][question_image'+n+']" />';
html += '</span>';
html += '<div class="clear10"></div>';
html += '</div>';
html += '<div class="fildtpart3 pos">';
html += '<label>Answer'+n+'</label>';
html += '<span><input type="text" class="validate[required]" id="" value=""></span>';
html += '<div class="clear10"></div>';
html += '</div>';
html += '</div>';
div.append(html);
i++;j++;n++;
// $(document).trigger('updated');
});
$(document).on('change','.browse-btn', function(){
var thumbnailId = $(this).prev().attr('id');
showMyImage1(this, thumbnailId)
})
});
function showMyImage1(fileInput,id) {
//console.log(fileInput);
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
//alert(id);
var img = document.getElementById(id);//$("#"+id)[0];
//alert(img);
//console.log(img);
img.file = file;
var reader = new FileReader();
reader.onload = (function(aImg) {
//alert(aImg);
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="appendData"></div>
<div class="fildtpart2" style="float:right">
Add More
</div>
The onchange event is called when user select a file, but at this time the file itself won't be accessible. To show it, you need the user to post it, usually via a <form>, then saving it. From there, you will be able to show it.
On many modern site, this process looks like being transparent, due to the use of AJAX call :
The onchange event call a jQuery function, that actually send the form (and the image) to a php file. This file save the image, then return the corresponding <img> tag, filled with the correct attribute src, that will be append on the page via the Ajax callback.
Related
I'm using touchTouch lightbox library in order to preview the images in a lightbox for a post that user wants to send.
This library is used on my back-end and works flawlessly (since the images are in a folder already), but in the front-end there is a form where the user may upload up to 20 images. The images will show their preview after the user has uploaded them (but before they have sent the form) and I want them to be clickable which would open a lightbox library.
The problem is that the library can't find the images location because they are not uploaded just yet.
I thought about using ajax to upload the images into a temporary folder and then remove the folder and its content once the form is submitted. Could this work (without refreshing the page or submitting the form), or are there any better alternative solutions for this?
JavaScript code to upload the files and show their preview + initialize lightbox library upon loading:
function ImgUpload() {
var imgWrap = "";
var imgArray = [];
$('.upload__inputfile').each(function () {
$(this).on('change', function (e) {
imgWrap = $(this).closest('.upload__box').find('.upload__img-wrap');
var maxLength = $(this).attr('data-max_length');
var files = e.target.files;
var filesArr = Array.prototype.slice.call(files);
var iterator = 0;
filesArr.forEach(function (f, index) {
if (!f.type.match('image.*')) {
return;
}
if (imgArray.length > maxLength) {
return false
} else {
var len = 0;
for (var i = 0; i < imgArray.length; i++) {
if (imgArray[i] !== undefined) {
len++;
}
}
if (len > maxLength) {
return false;
} else {
imgArray.push(f);
var reader = new FileReader();
reader.onload = function (e) {
var html = "<div class='upload__img-box'><a href='" + f.name + "' class='light-box' data-group='gallery'><div style='background-image: url(" + e.target.result + ")' data-number='" + $(".upload__img-close").length + "' data-file='" + f.name + "' class='img-bg'><div class='upload__img-close'></div></div></a></div>";
imgWrap.append(html);
iterator++;
touchTouch(document.body.querySelectorAll('.light-box')); // Initialize the lightbox library
// Ajax script here to upload the images to a temporary folder?
}
reader.readAsDataURL(f);
}
}
});
});
});
Basically you could create a data-url from an uploaded image (before uploaded) which will allow you to access it locally and treat it like it was a regular image.
imgInp.onchange = evt => {
const [...files] = imgInp.files
if (files) {
files.forEach(function(file) {
var img = new Image();
img.src = URL.createObjectURL(file)
img.style.width = "200px"
container.appendChild(img)
// do your touchTouch initialization on the new elements
// touchTouch(img);
img.onclick = function() {
alert('lightbox');
}
})
}
}
<form>
<p>Choose Images, then click for lightbox.</p>
<input accept="image/*" type='file' id="imgInp" multiple />
</form>
<div id="container">
</div>
Let's try with given code (not tested):
ImgUpload();
function ImgUpload() {
var imgWrap = "";
var imgArray = [];
$('.upload__inputfile').each(function() {
$(this).on('change', function(e) {
imgWrap = $(this).closest('.upload__box').find('.upload__img-wrap');
var maxLength = $(this).attr('data-max_length');
var files = e.target.files;
var filesArr = Array.prototype.slice.call(files);
var iterator = 0;
filesArr.forEach(function(f, index) {
if (!f.type.match('image.*')) {
return;
}
if (imgArray.length > maxLength) {
return false
} else {
var len = 0;
for (var i = 0; i < imgArray.length; i++) {
if (imgArray[i] !== undefined) {
len++;
}
}
if (len > maxLength) {
return false;
} else {
imgArray.push(f);
var object_url = URL.createObjectURL(f)
var html = "<div class='upload__img-box'><a href='" + f.name + "' class='light-box' data-group='gallery'><div style='background-image: url(" + object_url + ")' data-number='" + $(".upload__img-close").length + "' data-file='" + f.name + "' class='img-bg'><div class='upload__img-close'></div></div></a></div>";
imgWrap.append(html);
iterator++;
setTimeout(function() {
touchTouch(document.body.querySelectorAll('.light-box')); // Initialize the lightbox library
});
// Ajax script here to upload the images to a temporary folder?
}
}
});
});
});
}
<form>
<p>Choose Images, then click for lightbox.</p>
<input accept="image/*" type='file' id="imgInp" class="upload__inputfile" multiple />
</form>
<div id="container">
</div>
Attempt to convert the data obtained from the File interface to Base64 or DataURL.then set img src='data:image/png;base64str'
are there any better alternative solutions for this?
Yes! I would recommend these js libraries
Filepond by PQINA and Dropzone.js
Be advised that they both have image preview but not lightbox feature. But with Dropzone.js, this Stack Overflow thread might help How to add popup image in to dropzone thumbnail?
Hello I have the following code
function fileValidation() {
var fileInput = document.getElementById('filech');
var filePath = fileInput.value;
var allowedExtensions = /(\.jpg|\.jpeg|\.png|\.gif)$/i;
if (!allowedExtensions.exec(filePath)) {
alert('error .jpeg/.jpg/.png/.gif ');
fileInput.value = '';
return false;
} else {
//Image preview
if (fileInput.files && fileInput.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
document.getElementById('imagePreview').innerHTML = '<img src="' + e.target.result + '"/>';
};
reader.readAsDataURL(fileInput.files[0]);
}
}
}
<input id="filech" type="file" name="file_img[]" multiple onchange="return fileValidation()" />
<div id="imagePreview"></div>
To upload photos by
<input id="filech" type="file" name="file_img[]" multiple onchange="return fileValidation()" />
then show
<div id="imagePreview"></div>
I want to show all the pictures and not one
How to use the loop here and thank all
Well as you said you will need a loop, the easiest way would be to use a for loop, like this:
for (var i = 0; i < fileInput.files.length; i++) {
if (fileInput.files && fileInput.files[i]) {
var reader = new FileReader();
reader.onload = function(e) {
document.getElementById('imagePreview').innerHTML += '<img src="' + e.target.result + '"/>';
};
reader.readAsDataURL(fileInput.files[i]);
}
}
Note:
Note that I changed it to document.getElementById('imagePreview').innerHTML +=, so it keep printing all the iterated images, otherwise it will just override the preview with the last image content.
But the best practice is to create an img element on each iteration and append it to the preview div:
for (var i = 0; i < fileInput.files.length; i++) {
if (fileInput.files && fileInput.files[i]) {
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.src = e.target.result;
document.getElementById('imagePreview').appendChild(img);
};
reader.readAsDataURL(fileInput.files[i]);
}
}
Demo:
function fileValidation() {
var fileInput = document.getElementById('filech');
var filePath = fileInput.value;
var allowedExtensions = /(\.jpg|\.jpeg|\.png|\.gif)$/i;
if (!allowedExtensions.exec(filePath)) {
alert('error .jpeg/.jpg/.png/.gif ');
fileInput.value = '';
return false;
} else {
//Image preview
for (var i = 0; i < fileInput.files.length; i++) {
if (fileInput.files && fileInput.files[i]) {
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.src = e.target.result;
document.getElementById('imagePreview').appendChild(img);
};
reader.readAsDataURL(fileInput.files[i]);
}
}
}
}
<input id="filech" type="file" name="file_img[]" multiple onchange="return fileValidation()" />
<div id="imagePreview"></div>
I am working on this for the past 6 hours, and I am unable to figure this out.
I am trying to create a website, in which I choose a folder of images and I show them in my document. I get the first image to show, and then I get the following error in my console.
Uncaught DOMException: Failed to execute 'readAsDataURL' on 'FileReader': The object is already busy reading Blobs.(…)
I believe the issue is caused due to my for loop because the filereader is asynchronous. But I need to loop through the whole array for this, so what am I doing wrong?
I load the files ("will check to make sure I get only images later"), into an array, and then I read each file one at a time.
Before breaking down my code to functions I did everything in a single function and it works! I included the HTML + the original and current JS code. Thank you for taking the time to see this.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tiled Image Viewer</title>
<script src="js/tiv.js"></script>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="main-wrap">
<form name="uploadForm">
<input id="images" type="file" webkitdirectory mozdirectory directory name="myFiles"
onchange="readAndShowFiles();" multiple/>
<span id="list"></span>
</form>
</div>
</body>
</html>
Javascript Original:
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
// Have to check that this is an image though
// using the file.name TODO
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
Javascript Current:
function readAndShowFiles() {
console.log("Checkpoint2"); //test
var tiv = new tivAPI();
var array = tiv.getLoadedImages();
tiv.showLoadedImages(array);
}
function tivAPI(){
var imagesarray = new Array();
return{
loadImages: function(){
console.log("Loading Files"); //test
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
// Have to check that this is an image though
// using the file.name TODO
}
console.log(files.length); //test
return files;
},
getLoadedImages: function(){
imagesarray = this.loadImages();
console.log("Returning Files"); //test
console.log(imagesarray.length);
return imagesarray;
},
showLoadedImages: function(elem){
console.log("Showing Files"); //test
var files = elem;
var reader = new FileReader();
// Closure to capture the file information.
for (var i = 0; i < files.length; i++) {
var file = files[i];
reader.onload = (function(file) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
};
}
The reason why your code fails is that you are using the same reader variable on each subsequent loop iteration.
When Javascript parses your code, what happens is that all variables get moved to the top of the function, so your parsed code looks something like this:
function readAndShowFiles() {
var files = document.getElementById("images").files;
var reader;
var file;
var i;
for (i = 0; i < files.length; i++) {
file = files[i];
reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
})(file);
reader.readAsDataURL(file);
}
}
You could avoid this error quite simply by just moving all the logic inside the anonymous function like so:
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (var i = 0; i < files.length; i++) {
// Closure to capture the file information.
(function(file) {
var reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
})(files[i]);
}
}
Now you are using a unique reader variable for each file iteration. Note also that this could be simpler if you just did:
array.from(files).forEach(function(file) {
// code
})
Or just used the let variable (that way you will use a different FileReader every time):
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (let i = 0; i < files.length; i++) {
let file = files[i];
let reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
The for loop could be written easier with es6 leaving you with 2 less variables
function readAndShowFiles() {
var files = document.getElementById("images").files;
for (let file of files) {
let reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img src="', e.target.result,
'" title="', escape(file.name), '">'
].join('');
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
But if you want it super easy, why not just skip the FileReader altogether and avoid all functions & callbacks with URL.createObjectURL? Using createObjectURL you save CPU de/compiling the file to/from base64:
function showFiles() {
var files = document.getElementById("images").files;
for (let file of files) {
let img = new Image;
img.src = URL.createObjectURL(file);
img.title = file.name;
document.getElementById('list').appendChild(img);
}
}
the Problem occured because I was trying to use the same reader for every image.
Moving var reader = new FileReader();into the loop (ShowLoadedImages function), solved the issue and showed all the images like it was supposed to.
So I'm doing a simple multiple image upload script using javascript, but socket.io has to be used in order to get the image into the database. In order to run previews I have been taking event.target.result and putting it as the image src on a div. Is there any way I can store the this in an array for each image so that I can transfer it over the socket, and have it load on the other side? When I try to load it into an array, it's always undefined.
for (var i = 0; file = files[i]; i++) {
name[i] = files[i].name;
// if the file is not an image, continue
if (!file.type.match('image.*')) {
continue;
}
reader = new FileReader();
reader.onload = (function (tFile) {
return function (evt) {
var div = document.createElement('div');
var miniDiv = document.createElement('div');
div.id = "photoDiv";
div.innerHTML = '<img style="width: 120px; height: auto;" src="' + evt.target.result + '" />';
div.className = "photos";
var data = evt.target.result;
picture[i] = data;
document.getElementById('filesInfo').appendChild(div);
document.getElementById('previewDiv').appendChild(document.getElementById('filesInfo'));
};
}(file));
reader.readAsDataURL(file);
}
uploadFiles();
}
Don't make functions within a loop like that, it can lead to unexpected things.
I would suggest using JSHint, it's very helpful.
You made two mistakes:
1) You should pass i variable to your closure together with file.
2) The most important: reader.onload is a function that will be called not immediately, but in some delay, and as a result it will be called after uploadFiles() call. That's why you get an empty picture.
Try to rewrite your code as follows:
var done = 0;
var picture = [];
for (var i = 0; file = files[i]; i++) {
name[i] = files[i].name;
// if the file is not an image, continue
if (!file.type.match('image.*')) {
if (++done === files.length) {
uploadFiles();
}
continue;
}
reader = new FileReader();
reader.onload = (function (tFile, index) {
return function (evt) {
//[...]
picture[index] = data;
//[...]
if (++done === files.length) {
//the last image has been loaded
uploadFiles();
}
};
}(file, i));
reader.readAsDataURL(file);
}
HTML Code
<input type="file" accept="image/*" multiple webkitdirectory mozdirectory msdirectory odirectory directory id="fileURL"/>
Javascript Code:
var files,
file,
extension,
sum,
input = document.getElementById("fileURL"),
output = document.getElementById("fileOutput"),
holder = document.getElementById("fileHolder")
sizeShow = document.getElementById("filesSize");
input.addEventListener("change", function (e) {
files = e.target.files;
output.innerHTML = "";
sum = 0;
for (var i = 0, len = files.length; i < len; i++) {
file = files[i];
extension = file.name.split(".").pop();
if(extension=="jpg"||extension=="png"){
var size = Math.floor(file.size/1024 * 100)/100;
sum = size+sum;
output.innerHTML += "<li class='type-" + extension + "'>"+file.webkitRelativePath + file.name + " (" + size + "KB)</li>";
}else{
file.remove();
}
}
if(sum<1024*1024){
sizeShow.innerHTML = Math.floor(sum/1024*100)/100 + " MB";
}else if(sum>1024*1024){
sizeShow.innerHTML = Math.floor(sum/1024*1024*100)/100 + " GB";
}
}, false);
How do i get just the image in the file upload? accept="image/*" doesn't work for directory.
This does work but the statement file.remove() doesn't work at all.
I guess the input:file is read-only.
How do i solve this?
You can set input.files to a FileList (obtained from e.g. drag and drop), but you cannot create/modify a FileList. So you cannot modify the files of an input to e.g. only contain images.
What you can do, though, is uploading manually (through ajax), and only send files that have a type starting with "image/". See http://jsfiddle.net/WM6Sh/1/.
$("form").on("submit", function(e) {
e.preventDefault();
var files = $(this).find("input").prop("files");
var images = $.grep(files, function(file) {
return file.type.indexOf("image/") === 0; // filter out images
});
var xhr = new XMLHttpRequest();
xhr.open("POST", "/", true);
$(xhr).on("readystatechange", function(e) {
if(xhr.readyState === 4) {
console.log("Done");
}
});
var data = new FormData();
$.each(images, function(i) {
data.append(i, this); // append each image file to the data to be sent
});
console.log(
"Sending %d images instead of all %d files...",
images.length,
files.length
);
xhr.send(data);
});