I'm having a problem with the Javascript File API. First I'm checking if a form input is valued, and if the form input type=file name is image, I want it to get the image:
function checkIfValued() {
$("form input, textarea").change(function() {
// ifs ....
else if (name === "image") {
checkQrCode();
}
// else ifs ....
});
}
Getting the image:
function checkQrCode(evt) {
var qrcode = evt.target.files[0]; // create a FileList and take the first one
if (!qrcode.type.match("image.*")) { // check to see if it is an image
var $parentDiv = $(this).parent().parent();
$parentDiv.removeClass("has-success");
$parentDiv.addClass("has-error");
return;
}
var reader = new FileReader(); // create a FileReader
reader.onload = function (evt) {
var img = new Image(); // create a new image
img.src = event.target.result; // set the src of the img to the src specified
if ($("#qrcode").siblings().length != 0) { // if qrcode has siblings (function already executed)
$("#qrcode").siblings().remove(); // remove the siblings
}
$("#qrcode").parent().append(img); // append the img to the parent
$("#qrcode").siblings("img").addClass("img-thumbnail");
$("#qrcode").siblings("img").css("float", "left");
}
reader.readAsDataURL(qrcode);
}
When I used :
$("#qrCode").change(checkQrCode);
It worked, but when I use the first code it doesn't. I guess it has something to do with the event in the checkQrCode function (in the last code, the event is tied directly to the function and in the second code it has an if statement in the way).
Anyway, how can I fix it and if someone could explain the event/evt option, it would be very appreciated.
Your checkQrCode function expects to receive the triggered event, which is given as an argument in the event callback.
When you call it like this:
$("#qrCode").change(checkQrCode);
... then that even is being passed to the function by the callback. However in your first example you're ignoring the event and calling your event-expecting function with no arguments.
You'd want it to look more like this:
$("form input, textarea").change(function(evt) {
// ...
checkQrCode(evt);
Related
You can see my demo here.
I have simple html:
<input type="file" id="files" multiple>
<div class="preview"></div>
So when user choose image he can preview it, and when he click on upload button and add another images he also will see in preview that this images was added.
My problem that I want to add for each appended div a data attribute with index value of it.
My js:
$("#files").on("change", previewFiles);
function previewFiles() {
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
}
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
In my code there is a problem, when user choose image for the first time, data attribute is not created, if he upload image for the second time, appended before div or divs which was first will get data attribute, but new appended div or divs not.
What am I doing wrong?
P.S. I know that my input is clearing its value before uploading another images. All I want is related to preview block. Thanks in advance.
This is happening because you're trying to talk to an element that is not yet in the DOM - because onload is an asynchronous event.
In other words, the element with .preview__image does not get added until AFTER you're running preview.find('.preview__image').
There's various ways round this. One would be to use Promise.all() and convert your reader function to return a promise that gets resolved only once the onload callback has completed.
We could attach a then() to Promise.all, but more elegant would be to use await, which means we'll need to make your outer function asynchronous, by prefixing async to it.
All in all (I've commented the key changes):
$("#files").on("change", previewFiles);
async function previewFiles() { //<-- prefix with async
var preview = $('.preview');
var files = $(this)[0].files;
function readAndPreview(file) {
return new Promise(res => { //<-- reader func now returns promise...
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.onload = function(event) {
var conta = $('<div></div>').addClass("preview__image");
var img = $('<img>');
img.attr('src', event.target.result);
img.attr('title', file.name);
img.appendTo(conta);
conta.appendTo( preview );
res(); //...which is resolved once onload is complete
}
reader.readAsDataURL(file);
}
});
}
//now let's have a master promise that waits for the sub-promises to resolve
await Promise.all([...files].map(file => readAndPreview(file)));
//now we can talk to the updated DOM
var child = preview.find(".preview__image");
child.each(function(index) {
$(this).attr("data-index", index);
});
}
I'm currently working on a Rails 6 application using trubolinks. I'm working on a function to reaplace an avatar placeholder with the image selected upon upload. However, something weird is happening I'm declaring two variables, one is stated with a value the over does not.
document.addEventListener('readystatechange', event => {
if (event.target.readyState === "complete") {
/**
* Display the image in the file input when added to the form.
* Replace avatar with image selected.
*/
const profileAvatarBlock = document.getElementById('profile-avatar');
function showImage(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
let avatarPreview = document.getElementById('profile-avatar-preview');
let img =avatarPreview.children[1].setAttribute("src", e.target.result);
debugger;
['width', 'height'].forEach(attribute => {
img.removeAttribute(attribute)
});
debugger;
};
reader.readAsDataURL(input.files[0]);
}
}
profileAvatarBlock.addEventListener('change', function() {
showImage(this);
})
}
});
At first I thought that it was because of turbolinks so I add "turbolinks:load", but this didn't change anything. When I check for avatarPreview I get back back in the debugger but when I check img I get undefined. if I run avatarPreview.children[1].setAttribute("src", e.target.result); I also get it returned but if I assigned it to img is not working.
Why I cant declare a variable inside the callback? I want to understand dont care much about getting it to work.
You are calling setAttribute to assign e.target.result to the src attribute of the element. Then, you are assigning the return value from that function (which is always undefined) to img.
Try instead:
let img = e.target.result
If you really want to get the value from the children, you can try
let img = avatarPreview.children[1].getAttribute('src')`
I have an ajax function that is working. I have pasted the onComplete piece of the function below. It correctly returns the image in the following class(es): .paidpromopic .paidpromo-image .thumb.
onComplete: function (filename, json) {
if (json.error) {
App.error(json.error);
$('.paidpromopic .paidpromo-image .thumb').show();
} else if (json.success) {
var image = new Image();
image.src = json.success;
image.onload = function () {
$('.paidpromopic .paidpromo-image .thumb').attr('src', image.src).show();
$('.avatar-next').show();
}
[HERE IS WHERE I'M HOPING TO WRITE ONE LINE TO UPDATE $currentimage]
}
Following (.paidpromopic .paidpromo-image .thumb), I have a div (id="new_image_name"). Inside that div, I have the variable $currentimage.
I would like to update the variable $currentimage. The php function called by the ajax function creates a session variable named 'newimage_name'. In the same php function, I have also set $currentimage = 'newimage_name'.
Is there are line of code I can include in the onComplete piece above that will update div id="new_image_name"
I have the following code, which executes before a form is submitted.
I'm changing an image then redrawing a canvas before saving that image.
the problem I have is that it doesn't work as intended (instead it saves the image with the previous image src attribute)...
If I step through it in firebug the correct image is saved, which leads me to think there needs to be a delay before it saves....
How do I do it?
$('#WindowForm').submit(function () {
if (isInternal)
{
imgsrc = $(".externalColor.selected-color").find('img').attr("src");
draw();
var canvas = document.getElementById('myCanvas');
context.drawImage(can, 0, 0)
var dataURL = canvas.toDataURL();
$("#ImageData").val(dataURL);
return true;
}
});
Note that there is an onload in the draw method too:
var img = new Image();
img.src = imgsrc;
img.onload = function () {
pattern = context.createPattern(img, "repeat");
context.fillStyle = pattern;
DrawContext(context);
};
The problem is very likely that you need to wait for the image to load in your draw() function. The best way would be to include a callback parameter in your function, so you could save after it is finished.
function draw(callback){
//...
img.onload = (function(callback){
return function(){
//...create your pattern...
callback.call();
};
})(callback);
}
draw(function(){ /* ...save image... */ });
You could also use something like setTimeout(function(){ /*...save image...*/ }, 1); but this isn't the best solution.
I'd approach it like this:
$( 'form' ).on(
'submit',
function( evt ) {
var form = $( this );
evt.preventDefault(); // prevent the form submission
// ...do stuff...
// delay the submission
window.setTimeout(
function() {
form.off( 'submit' ); // remove this event handler
form.submit(); // manually submit the form
},
500 // or whatever
);
}
);
This basically stops the form from being submitted until everything is sorted, then manually submits it. You might want to combine this with #Evilzebra's answer and stick the timeout function in an onload event for the created image, however the basic principles will be the same (prevent submission, then manually submit once ready).
N.B. you need to unbind the event handler before the manual submit, or the submit event will fire again and just keep running in a loop!
Looking through your code I think I understand what you're trying to do, and why its not working. First off I think your example code has some errors that shouldn't be there. I'm going to assume that imgsrc should be img.src at which point your making an asynchronous call to load a new image, which most likely won't be done until after you've competed the rest of this function. A quick and easy way to remedy this is to create a flag that lets the img.onload know if it should be saving the image or not. In the below example I key off of isInternal
$('#WindowForm').submit(function (e) {
if (isInternal)
{
e.preventDefault();
imgsrc = $(".externalColor.selected-color").find('img').attr("src");
// this will then cause the img.onload to fire
};
});
function pleaseSubmitMe(){
draw();
var canvas = document.getElementById('myCanvas');
context.drawImage(can, 0, 0)
var dataURL = canvas.toDataURL();
$("#ImageData").val(dataURL);
// alright now that thats done, lets submit this bad boy
isInternal= false; // we need to set this to false so we actually try submitting
$('#WindowForm').submit();
}
img.onload = function () {
pattern = context.createPattern(img, "repeat");
context.fillStyle = pattern;
DrawContext(context);
// if I'm Internal lets send this image to be submitted
if(isInternal){
pleaseSubmitMe();
}
};
The following javascript is run onload:
var shop_checkout = {
setup: function() {
var img = document.createElement('img');
img.src = '/public/styles/images/confirm_payment.png';
img.id = 'confirm_payment';
img.alt = 'Confirm Payment: submit order and charge my card';
var span = app.get('submit_checkout');
console.log('found the span');
span.innerHTML = '';
console.log('emptied the span');
span.appendChild(img);
console.log('appended the image');
img.onclick = shop_checkout.onsubmit;
console.log('set the onclick');
},
onsubmit: function() {
// irrelevant
},
submitted: function() {
// irrelevant
},
oncheckout: function(json, status) {
// irrelevant
}
};
app.onload(shop_checkout.setup);
(app.onload() is a simple function which builds up a list of functions to fire on load, and app.get() is a simple shortcut for document.getElementById(). This is shown at the bottom.)
Most of the time, this works grand. Now and again, it gets as far as emptying the span, but fails to append the image. The error message produced is: “Uncaught Error: NotFoundError: DOM Exception 8”.
Now, DOM Exception 8 is a not found error, which suggests that the img doesn’t exist, even though I created it just a couple of lines previously. What could be going on here?
I’m guessing that the img DOM element doesn’t necessarily exist, even though the variable referring to it does exist. Is that right? And what can I do about it?
Here's the app code:
var app = {
get: function(id) {
return document.getElementById(id);
},
onload: function(func) {
// http://www.dustindiaz.com/top-ten-javascript/
var oldonload = window.onload;
if (typeof window.onload !== 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
};
}
}
};
It looks like your <span> element never gets attached to the DOM. You can only find things by id if they descend from the document (<html>).
Make sure to append the <span> to the document before you try to find anything that gets attached to it (directly or to descendants).
One reason is related to elements that have asynchronous data. Or elements that are added to the DOM after the document is loaded.
In jQuery, the following command can be used
$ ('element').ready (readyFn);
https://learn.jquery.com/using-jquery-core/document-ready/
And in Angular , element change event can be useful