I have three image upload fields in my form. I am trying to preview the images before uploading. Till now i have been able to preview only one at a time. but i am trying to preview all of them simultaneously. Like
I am currently previewing like this.
function previewImages() {
var $preview = $('#preview').empty();
if (this.files) $.each(this.files, readAndPreview);
function readAndPreview(i, file) {
if (!/\.(jpe?g|png|gif)$/i.test(file.name)){
return alert(file.name +" is not an image");
} // else...
var reader = new FileReader();
$(reader).on("load", function() {
$preview.append($("<img/>", {src:this.result, height:500}));
});
reader.readAsDataURL(file);
}
}
$('#file-input').on("change", previewImages);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group">
<label>Image 2</label>
<input id="file-input" type="file" multiple>
<div id="preview"></div>
</div>
<img id='img-upload'/>
</div>
<div class="form-group">
<label>Image 3</label>
<input id="file-input" type="file" multiple>
<div id="preview"></div>
</div>
You have some problems in your code.
Never use duplicate id's . That's the main reason your code works only for the first input type file. You use id='file-input' on all inputs. That's not correct HTML wise. Use classes instead or other html attributes.
You select the preview container but only the first one. $preview = $('#preview').empty(); where by writing $('#preview'), jquery finds the first item with id preview, selects it, empties it and then ignores all other divs with the same id. This is firstly because you don't select the corresponding preview div to the changed input file ( no connection between them in your var declaration ) and because again you use duplicate ( multiple ) id's in your HTML structure. Which will generate a lot of ' strange ' errors.
But even if you would use classes instead of id's ( class='preview' ) it won't exactly work because it will just select the first div with class preview and append all previews to that div.
So to make a connection between input and it's preview, use something like var $preview = $(this).next('.preview').empty(); . This way you know jQuery will select the next sibling with class preview of the changed input.
Alternatively ( if your HTML structure should change and preview is not exactly after the input ) you should use $(this).siblings('.preview').empty()
The most important thing you should learn from this answer is NOT to use same id`s on the page. And when you have multiple items with corresponding elements, select them accordingly.
function previewImages() {
const $preview = $(this).next('.preview').empty();
if (this.files) $.each(this.files, readAndPreview);
function readAndPreview(i, file) {
if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
return alert(file.name + " is not an image");
} // else...
const reader = new FileReader();
$(reader).on("load", function() {
$preview.append($("<img/>", {
src: this.result,
height: 100
}));
});
reader.readAsDataURL(file);
}
}
$('.file-input').on("change", previewImages);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group">
<label>Image 1</label>
<input class="file-input" type="file" multiple>
<div class="preview"></div>
</div>
<img class='img-upload' />
<div class="form-group">
<label>Image 2</label>
<input class="file-input" type="file" multiple>
<div class="preview"></div>
</div>
<img class='img-upload' />
<div class="form-group">
<label>Image 3</label>
<input class="file-input" type="file" multiple>
<div class="preview"></div>
</div>
SIDE NOTE . You should use const and/or let instead of var when declaring variables.
var $preview=null;
function previewImages() {
$preview = $(this).next('#preview').empty();
if (this.files)
$.each(this.files,function(index,file){
readAndPreview(index,file);
});
}
function readAndPreview(i, file) {
if (!/\.(jpe?g|png|gif)$/i.test(file.name)){
return alert(file.name +" is not an image");
} // else...
var reader = new FileReader();
$(reader).on("load", function() {
$preview.append($("<img/>", {src:this.result, height:100}));
});
reader.readAsDataURL(file);
}
$('.file-input').on("change", previewImages);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="form-group">
<label>Image 2</label>
<input class="file-input" type="file" multiple>
<div id="preview"></div>
</div>
<img id='img-upload'/>
</div>
<div class="form-group">
<label>Image 3</label>
<input class="file-input" type="file" multiple>
<div id="preview"></div>
</div>
Try this. css ids are unique for a page. if you want to access multiple elements with same selector use class.
Related
In the website is the ability to ask a question and provide answers with images. We have used some website template and now trying to change functionallity. So I'm trying to achieve if image clicked, pop ups upload window (to select picture) and after clicking ok button, it should be shown near the answer (look image below, noted in red).
There is the ability to add more input fields by clicking the button. It generating HTML like this:
<div class="row">
<div class="another-div">
<div class="image-answer">
<input type="image" name="answers_images[]" id="test-1" src="image.png">
<input type="file" name="answers_images[]" id="file-1" style="display: none;">
</div>
<label class="text-answer">
<input type="text" name="answers[]" placeholder="Type here your answer">
</label>
</div>
</div>
If user clicks the button it will add another row with incremented ids, so it will be test-2, file-2
after clicking button once again it will add row with incremented ids once again test-3, file-3 and so on.
How can I detect input clicks (via JQuery) if ids are auto incremental? And after it do some changes with JQuery only with clicked ($this) image.
Something like this:
$("#file-1...").click(function(e) {
e.preventDefault();
// Some code here, I have working code to achieve what I want
// But problem, that I have no idea how to detect those clicks with dynamic ids
});
$("#test-1").change(function(e) {
e.preventDefault();
});
If you want to do event handling on dynamically added content, the trick is using the .on() method with a non-dynamically added parent element, and providing a selector argument. This approach use classes instead of ids. You cans still have ids, but the this variable becomes the element selected, so you don't really need them.
<div class="parent">
<div class="image-answer dynamically-added" id="answer1"></div>
<div class="image-answer dynamically-added" id="answer2"></div>
<div class="image-answer dynamically-added" id="answer3"></div>
</div>
$('.parent').on('click', '.image-answer', function() {});
Say if you clicked on the second .image-answer, in the event handler function this would be the same as $('#answer2')[0]. So to make it a jquery object you just have to $(this). And if you want to access a child elemet, just use the .find() method.
$(this).find('input[type="file"]').on('change', function previewFile() { ... })
Also, inputs dont' have a default action for click, so e.preventDefault() isn't necessary.
You can use class selector instead of id's to have a more dynamic approach of what you are trying to achieve. Also with class you will be writing less code and getting the same results.
You can still use the id's increment system to make sure the id's are unique in the DOM.
Add class to your input image and input file and and watch a change on those classes. You can use prev() method you select the previous input of which the change was made on using $(this)
//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
e.preventDefault()
//add file to input / trigger click
$(this).next('input').click();
})
//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
e.preventDefault()
//get the sibling img src of the change file
var prevInput = $(this).prev('input')
//Call read file preview
previewFile(this, prevInput);
});
Pass your input and prev input image where the change happened to your previewImage function so that the file can be previewed in the image location using file Reader API.
//Preview img
function previewFile(input , previewSrc) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
//load file into actual preview img
$(previewSrc).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]); // convert to base64 string
}
}
I have added the functionality of adding more answers as well and you can upload image and preview images on those new answers as well using event Delegation as the elements are being append dynamically.
Complete Working Demo:
//row with incremented ids
var counter = 2
//Add more
$('#add_more').click(function() {
//add new answer
var newRow = `<div class="another-div">
<div class="image-answer">
<input type="image" class='add_image' name="answers_images[]" id="test-` + counter + `" src="https://img.icons8.com/dotty/80/000000/upload.png">
<input type="file" class='select_file' name="answers_images[]" id="file-` + counter + `" style="display: none;">
</div>
<label class="text-answer">
<input type="text" name="answers[]" id="answer-` + counter + `" placeholder="Type here your answer">
</label>
<button class="remove_answer"><i class="fas fa-window-close"></i></button>
</div>`
//append new answers
$('.row').append(newRow)
counter++ //increare counter
})
//remove answer
$(document).on('click', '.remove_answer', function() {
//remove the answer div
$(this).parent().remove()
counter-- //decrease counter
})
//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
e.preventDefault()
//add file to input / trigger click
$(this).next('input').click();
})
//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
e.preventDefault()
//get the sibling img src of the change file
var prevInput = $(this).prev('input')
//Call read file preview
previewFile(this, prevInput);
});
//Preview img
function previewFile(input, previewSrc) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
//load file into actual preview img
$(previewSrc).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]); // convert to base64 string
}
}
.add_image {
width: 80px;
height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<div class="row">
<div class="another-div">
<div class="image-answer">
<input type="image" class='add_image' name="answers_images[]" id="test-1" src="https://img.icons8.com/dotty/80/000000/upload.png">
<input type="file" class="select_file" name="answers_images[]" id="file-1" style="display: none;">
</div>
<div id="preview_img"></div>
<label class="text-answer">
<input type="text" name="answers[]" id="answer-1" placeholder="Type here your answer">
</label>
</div>
</div>
<br>
<button id="add_more">
Add New Answer
</button>
How can i preview the selected ico with multiple inputs? The problem is that it select the input by id which is everywhere the same. How can i run the js with a indefined amount of inputs? The amount of inputs depends on the number of loops it ran. What can i do to make it work with multiple inputs
for ($x = 1; $x <= $number; $x++) {
$bar .= '<input type="text" name="title[]" placeholder="Name '.$x.'" id="title" value=""><input type="file" accept="video/*" name="file[]"/><input type="file" name="" class="icofile" id="icofile" accept="image/*" name="icon[]" hidden>
<label for="icofile" class="icofile" id="selector"><img src="../images/none.png"></label><br>';
}
}
?>
<script type="text/javascript">
var loader = function(e){
let file = e.target.files;
//let show= "<span>Selected file : </span>"+file[0].name;
let show= "<img src='none.png'>";
let output= document.getElementById("selector");
output.innerHTML = show;
output.classList.add("active");
if(file[0].type.match("image")){
let reader = new FileReader();
reader.addEventListener("load", function(e){
let data=e.target.result;
let image= document.createElement("img");
image.src=data;
output.innerHTML="";
output.insertBefore(image,null);
output.classList.add("image");
});
reader.readAsDataURL(file[0]);
}else{
let show="<img src='none.png'>";
//show= show+file[0].name;
output.innerHTML=show;
output.classList.add("active");
if(output.classList.contains("image")){
output.classList.remove("image");
}
}
};
let fileInput = document.getElementById("icofile");
fileInput.addEventListener("change", loader);
</script>
You got quite some redundant (repetitive) code that you could remove. Using Object URLs could make it easier for you if you ditch the unnecessary FileReader that waste time encoding decoding long base64 urls.
also you don't have uniq id's throughout your html. and you used name twice on the icofile input... so this problems have to be fixed first before you move on.
also the label with the for="icofile" will only point to the first input so it also needs to be uniq.
globalThis.preview = function preview (evt) {
const file = evt.target.files[0]
// Get matching label
const output = document.querySelector(`label[for=${evt.target.id}]`)
// Reuse the existing image instead of clearing and rebuilding
const img = output.querySelector('img')
output.classList.add('active')
output.classList.remove('image')
// This if statement is almost useless since
// accept="image/*" ensure that you only get images
if (file.type.match('image')) {
img.src = URL.createObjectURL(file)
output.classList.add('image')
} else {
img.src = 'https://dummyimage.com/50x50/ff0000/000000&text=None'
}
}
<!-- for loop that did output this: -->
<!-- foreach id in this list append an index -->
<input type="text" name="title[]" placeholder="Name" id="title-1">
<input type="file" name="file[]" accept="video/*" />
<input type="file" name="icon[]" accept="image/*" onchange="preview(event)" id="icofile-1" hidden>
<label for="icofile-1" class="icofile">
<img width="50" height="50" src="https://dummyimage.com/50x50/ff0000/000000&text=None">
</label>
<hr> <!-- for better look/seperation without css. -->
<input type="text" name="title[]" placeholder="Name" id="title-2">
<input type="file" name="file[]" accept="video/*" />
<input type="file" name="icon[]" accept="image/*" onchange="preview(event)" id="icofile-2" hidden>
<label for="icofile-2" class="icofile">
<img width="50" height="50" src="https://dummyimage.com/50x50/ff0000/000000&text=None">
</label>
I'm trying to have the filename of a chosen file appear in the body of a file picker. This is an Asp.Net Core view. I have the following file input:
<div class="form-group file-upload-wrapper">
<input id="the-file" type="file" name="name" onchange="handleFile()" />
<label class="custom-file-label" for="the-file">Choose a file</label>
</div>
I want the filename to appear in place of the Choose a file text.
My JS looks like this:
#section Scripts {
<script language="javascript">
function handleFile(file) {
console.log(file);
}
As it currently stands, I'm getting an error:
ReferenceError: handleFile is not defined
My assumption is that handleFile needs to look something like this:
document.getElementById("the-file").title = file.files[0].name;
Am I on the right lines with this, and why am I getting the above error?
JSFiddle here
You can use event handler for that. Remove onchange attribute form that element and use below:
document.getElementById('the-file')
.addEventListener('change', (e) => {
handleFile(e)
});
function handleFile(file) {
console.log(file.target.value);
}
Here is the snippet:
document.getElementById('the-file')
.addEventListener('change', (e) => {
handleFile(e)
});
function handleFile(file) {
console.log(file.target.value);
log(file.target.value);
}
function log(msg) {
document.getElementById('result').innerText = msg;
}
<div class="form-group file-upload-wrapper">
<input id="the-file" type="file" name="name" />
<label class="custom-file-label" for="the-file">Choose a file</label>
</div>
<pre id="result"></pre>
From the documentation , you need to modify the code like below:
<div class="custom-file">
<input type="file" class="custom-file-input" id="the-file" name="name"/>
<label class="custom-file-label" for="the-file">Choose a file</label>
</div>
#section Scripts
{
<script>
document.getElementById('the-file').addEventListener('change', function (e) {
var fileName = document.getElementById("the-file").files[0].name;
var nextSibling = e.target.nextElementSibling
nextSibling.innerText = fileName
});
</script>
}
You could refer to Bootstrap 4 File Input for more details.
why am I getting the above error?
The code within the fiddle is different from the OP. I'll take the code on Fiddle as an example.
First of all, note that you added a bootstrp.min.js reference, however, you didn't add a jquery for it. Because bootstrap.min.js has a dependency on jquery, it throws before the handleFile() function is defined.
That's why we could see an error of util.js:56 Uncaught TypeError: Cannot read property 'fn' of undefined at util.js:56 in the console. To fix that issue, either add a jquery.min.js before the bootstrap.min.js:
Or change the loading behavior of your js like:
And then you mix up the js statement with a function reference by :
<input id="the-file" type="file" name="name" onchange="this.handleFile" />
Note you should put a js statement instead of a single function reference within onchange="...". That being said, you could fix it by:
<input id="the-file" type="file" name="name" onchange="handleFile(event.target.files)" />
Your handleFile() uses a this that points to the window at runtime. As a result, it throws when invoked. Since we have passed the files as the arguments, change the function as below:
function handleFile(files) {
console.log(files);
}
<div class="form-group file-upload-wrapper">
<script>
const handleFile = file => {
console.log(file);
}
</script>
<input id="the-file" type="file" name="name" value ="file" onchange="handleFile(value)" />
<label class="custom-file-label" for="the-file">Choose a file</label>
</div>
I've been trying to make this work for a little while now.
I thought the following code would work since I'm getting the value from the input and setting the background-image URL to said value.
Thanks!
The code inside of the head tag.
<script type="text/javascript">
function loadImg() {
var imageUrl = $('#hostImage').attr('value')
document.getElementById("upload-success-bg").style.backgroundImage=imageUrl
}
</script>
<style>
#upload-success-bg {
background-image: url();
}
</style>
Input field code
<div class="status">
<input class="image-url" type="text" id="hostImage" name="hostImage" required="true"
value="URL LOADS HERE">
</div>
Where I would like to the image to show
<div class="dropzone upload-success" id="upload-success-bg">
<div class="info"><p>Drag image file here</p><p>Or click here to select image</p></div>
<input type="file" required="" class="input" accept="image/*"></div>
If you wish to use an URL for backgound and background-color CSS properties you have to use the url() syntax even within javascript, so changing your code to the following should work:
document.getElementById("upload-success-bg").style.backgroundImage = "url(" + imageUrl + ")"
In jquery, you can do it this way:
function loadImg() {
var imageUrl = $('#hostImage').attr('value')
$("#upload-success-bg").css("background-image", "url(" + imageUrl + ")");
}
A File object does not have a URL property. A Blob URL or data URL can be created which points to the uploaded file. A Blob URLs lifetime is linked to the document which created the URL. A data URL string
data:[<mediatype>][;base64],<data>
can be opened at a different window or browser.
You can use FileReader to convert File object to a data URL and set the <input type="text"> value to the FileReader instance result.
const input = document.querySelector("#file");
const [label] = input.labels;
const upload = document.querySelector("#upload-success-bg");
const uploadURL = document.querySelector("#hostImage");
const reader = new FileReader();
reader.addEventListener("load", e => {
const {result} = reader;
upload.style.backgroundImage = `url(${result})`;
hostImage.style.width = `calc(${result.length}px)`;
hostImage.value = result;
});
input.addEventListener("change", e => {
const [file] = input.files;
console.log(file)
if (file && /^image/.test(file.type)) {
reader.readAsDataURL(file);
}
});
#file {
display: none;
}
label[for="file"] {
white-space: pre;
}
<div class="dropzone upload-success" id="upload-success-bg">
<label class="info" for="file">
Drag image file here
Or click here to select image
</label>
<input type="file" required="" id="file" class="input" accept="image/*"></div>
<div class="status">
<input class="image-url" type="text" id="hostImage" name="hostImage" required="true" readonly="true" value="URL LOADS HERE">
</div>
I am looking to have the label be used for the selection of the item (so Choose File) is gone.
Here is my HTMl:
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image">Upload Image</label>
<input style="display:none;" class="uploadBtn" type="file" name="broker[image]" id="broker_image">
</div>
It works, although I do not have the image to actually have the name shown (which is part of the input tag)
Here is the js to try to make it show (which is 100% editable):
document.getElementsByClassName("trigger").onClick = function () {
document.getElementsByClassName("uploadFile").style.display = 'block';
document.getElementsByClassName("uploadFile").value = this.value;
};
Tried:
document.getElementsByClassName("trigger").onClick = function () {
document.getElementsByClassName("uploadFile")[0].style.display = 'block';
document.getElementsByClassName("uploadFile")[0].value = this.value;
};
With no luck.
JSFiddle:
http://jsfiddle.net/07zw6h3q/
I am lost on to make just the file name show, without some crazy javascript add in. Something simple. Using Rails 4.2 and Bootstrap.
You need to use the change event.
$('.uploadBtn').change(function(){
var a = $(this).val().split('\\');
$('.trigger').html(a[a.length - 1]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image">Upload Image</label>
<input style="display:none;" class="uploadBtn" type="file" name="broker[image]" id="broker_image">
</div>
Also you can show a preview of the file (if it's image of course) Preview an image before it is uploaded
JSFiddle
HTML
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image" style="border : solid 1px lightgrey; padding : 5px">Upload Image</label>
<input style="display:none"; class="uploadBtn" type="file" name="broker[image]" id="broker_image" />
<label id="broker_image_name">no selected file</label>
</div>
JavaScript
var upload_button = document.getElementById("broker_image");
upload_button.addEventListener("change", function() {
var file_name = upload_button.value;
/*
the file come like this : C:\fakepath\file.htm
so we only need the file name
*/
file_name = file_name.split("\\"); // preventing \ to be escaped himself
var file_name_length = file_name.length;
file_name = file_name[file_name_length - 1];
var broker_image_name = document.getElementById("broker_image_name");
broker_image_name.innerHTML = file_name;
});
I used the event change to change the file name, which is an additional label I put right after your button (that I stylized a bit by the way) and that is updated with the file name.
You see a commented part for the file name value because when getting back the file name after the user select one, it comes with the pattern C:\fakepath\myFile.htm so I extracted only the file by splitting the string.