The following are the variables i have declared in javascript script tag.
The Variables are declared globally and initialized in the inner function which is inside another function (outer).
The problem is; when i try to check the variables using console.log i get "undefined". I don't know why.
I have read the variable hoisting, scope etc of Javascript variables. But i am unable to solve it. Please help.
var width;
var height;
var ratio;
var nwidth;
var nheight;
var wbool;
var imgsrc;
var canvas;
$("#myimage").change(function(){
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
}
}
console.log(window.nwidth + "" + wbool);
You're console.log()'ing nwidth before it has been assigned.
image.onload is an asynchronous operation (think like a callback). the console.log() is firing before the variable data has been assigned.
Try moving the log into the onload function.
var width;
var height;
var ratio;
var nwidth;
var nheight;
var wbool;
var imgsrc;
var canvas;
$("#myimage").change(function(){
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
// it will be defined here, because you are doing
// it inside the async operation.
console.log(window.nwidth);
}
}
// this will be undefined, because the var has not been set
// by the time this line executes.
console.log(window.nwidth + "" + wbool);
why does this happen?
async operations, like ajax calls ( or file loading in your code), take time to complete. Since javascript runs on a single thread, if the app were to stop and wait for the operation to complete, then the browser would lock up. Not good!
So, async operations are skipped over, and the callback portion is executed once the operation completes. This means your code is executing like this:
start loading the file
console.log() // undefined
loading complete, and the variable has been defined.
To trigger a custom event, you could do something like this:
$("#myimage").change(function(){
readURL(this);
});
// SET UP LISTENER FOR YOUR CUSTOM EVENT
$(document).on('fileWasLoaded', function () {
console.log(window.nwidth + "" + wbool);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
// TRIGGER YOUR CUSTOM EVENT WHEN THE IMAGE IS LOADED
$(document).trigger('fileWasLoaded');
}
}
Related
I'm trying to valdiate a multiple image upload using jQuery. The validation proccess get the images dimensions and check if it is greater than 338x450. But I know very little about javascript. Can someone help me? Here's the code I'm trying:
HTML:
<input class="form-control" id="ads-photos" name="ads-photos[]" type="file" accept="image/jpeg, image/jpg" multiple />
javascript
var isFilesOk;
$('#ads-photos').change(function (evt) {
validateDimensions();
});
function validateDimensions() {
var fi = document.getElementById('ads-photos');
if (fi.files.length > 0) {
for (var i = 0; i <= fi.files.length - 1; i++) {
var fileName, fileExtension, fileSize, fileType, dateModified;
fileName = fi.files.item(i).name;
fileExtension = fileName.replace(/^.*\./, '');
if (fileExtension == 'jpg' || fileExtension == 'jpeg') {
if (readImageFile(fi.files.item(i))) {
alert(isFilesOk);
} else {
alert(isFilesOk);
}
}
}
}
// GET THE IMAGE WIDTH AND HEIGHT USING fileReader() API.
function readImageFile(file) {
var reader = new FileReader(); // CREATE AN NEW INSTANCE.
reader.onload = function (e) {
var img = new Image();
img.src = e.target.result;
img.onload = function () {
var w = this.width;
var h = this.height;
if (this.width >= 338 && this.height >= 450) {
isFilesOk = true;
} else {
isFilesOk = false;
}
}
};
reader.readAsDataURL(file);
return isFilesOk;
}
}
A example link
The above code needs analyze all the images sended by the user to know if they all have minimum required dimensions. But I'm not getting it to work.
The issue here is that the readImageFile method is asynchronous. The work it's doing happens later, after it's executed when the onload event is triggered on the file.
The best solution will depend a little on the environment you're targeting (eg: whether you need to support old IE, etc), but if you're able to use modern JS with Promises, this will be easier.
Basically, for each file that's selected, you want to get a Promise indicating whether the file is okay or not. A good way to do this is map the array to an array of Promises and use Promise.all to get a callback when they're all done.
Here's the method to get that for a single file:
function fileIsValid(file) {
return new Promise((resolve) => {
var reader = new FileReader(); // CREATE AN NEW INSTANCE.
reader.onload = function (e) {
var img = new Image();
img.src = e.target.result;
img.onload = function () {
var w = this.width;
var h = this.height;
const isValid = w >= 338 && h >= 450;
resolve(isValid);
};
};
reader.readAsDataURL(file);
});
}
And then using this method:
const fi = document.getElementById('ads-photos');
const promises = [];
for (let i = 0; i <= fi.files.length - 1; i++) {
const file = fi.files.item(i);
if (/\.jpe?g$/.test(file.name)) {
promises.push(fileIsValid(file));
}
}
Promise.all(promises).then((results) => {
// results is an array of booleans
const allValid = results.every(Boolean);
if (allValid) {
alert('Yeah!');
} else {
alert('Nope');
}
});
There's a few things to note here:
you could make it shortcut once a single error is found by making the promise reject if it's invalid, and Promise.all then won't wait for any other pending promises. This is a bit of a style question about whether failed validation should result in a rejected promise. Up to you on that. In this case, it's reading from a local file system and I presume they'll be selecting a relatively small number of files, so short-circuiting probably won't make any noticeable difference.
it doesn't handle actual errors (eg: what if a file is unable to be loaded?).
I am attempting to access the dimensions from my uploaded image before I post it to the post upload endpoint. If you look, there is a variable issueArrived that is supposed to determine if the image is too small and if so negate the upload process. Unfortunately, the image doesn't access the dimensions check until after the upload to the server commences. Any help would be appreciated.
// internal function that creates an input element
var file = newElement("input", 0, "");
// sets the input element as type file
file.type = "file";
file.multiple = "multiple";
file.name = "photoupload[]";
file.accept = "image/*";
var issueArrived = false;
file.onchange = function(e) {
// internal function that creates an image element
var img = newElement("img", 0, "");
img.onload = function(){
var cw = img.width;
var ch = img.height;
if(cw < 500 || ch < 500) {
alert("Photo must be atleast 500x500");
issueArrived = true;
return false;
}
}
img.src = URL.createObjectURL(this);
if (issueArrived) return;
var formdata = new FormData();
formdata.append("photoupload[]", this);
var x = new XMLHttpRequest();
x.open("POST", "/media/uploadphotos", true);
x.send(formdata);
}
file.click();
onchange method is async. You can't expect to set the value from issueArrived inside it an access it outside.
You can rewrite this way:
file.onchange=function(e){
// internal function that creates an image element
var img=newElement("img",0,"");
img.onload=function(){
var cw=img.width;
var ch=img.height;
if(cw<500||ch<500){
alert("Photo must be atleast 500x500");
} else {
var formdata=new FormData();
formdata.append("photoupload[]",this);
var x=new XMLHttpRequest();
x.open("POST","/media/uploadphotos",true);
x.send(formdata);
}
}
img.src=URL.createObjectURL(this);
}
file.click();
or, better, you can create a function that gets the file and returns a promise.
function checkSize(fileToCheck) {
return new Promise( function(resolve, reject) {
var img = newElement("img",0,"");
img.onload=function(){
var cw=img.width;
var ch=img.height;
cw<500||ch<500 ? reject() : resolve()
}
img.src=URL.createObjectURL(fileToCheck);
})
}
...
file.onchange=function(e){
checkSize(this)
.then( function() { /* success handler */ } )
.catch( function() { /* error handler */ } )
}
I'm trying to make a small snippet to preview images before uploading them:
$.fn.previewImg=function($on){
var input = this;
try{
if (this.is("input[type='file']")) {
input.change(function(){
var reader = new FileReader();
reader.onloadend = function(){
for (var i = 0; i < $on.length; i++) {
if (/img/i.test($on[i].tagName)) $on[i].src = reader.result;
else $on[i].style.bakgroundImage = "url("+reader.result+")";
}
};
});
}else throw new exception("Trying to preview image from an element that is not a file input!");
}catch(x){
console.log(x);
}
};
I'm calling it like:
$("#file").previewImg($(".preview_img"));
but the onloadend function is never called.
FIDDLE
Actually , you got to specify the file and instruct the fileReader to read it.
Below is the corrected code.
$.fn.previewImg=function($on){
var input = this;
try{
if (this.is("input[type='file']")) {
input.change(function(evt){
var reader = new FileReader();
console.log("Input changed");
reader.onloadend = function(){
console.log("onloadend triggered");
for (var i = 0; i < $on.length; i++) {
if (/img/i.test($on[i].tagName)) $on[i].src = reader.result;
else $on[i].style.bakgroundImage = "url("+reader.result+")";
}
};
//get the selected file
var files = evt.target.files;
//instruct reader to read it
reader.readAsDataURL(files[0]);
});
}else throw new exception("Trying to preview image from an element that is not a file input!");
}catch(x){
console.log(x);
}
};
$("#file").previewImg($(".preview_img"));
i have a input file which is used to upload images. However, I have to validate its size before upload. As below is the code I've tried on.
I have a parent function, that calls the method,ValidateImageSize( ):
$('input:file').change(function (e) {
if (ValidateImageSize(this))
// do something
else
alert("wrong size");
});
and the method shown as below:
var ValidateImageSize = function (input) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
return this.height == 40 && this.width == 40 ? true : false;
}
img.src = e.target.result;
}
reader.readAsDataURL(input.files[0]);
};
The method ValidateImageSize() always returns 'undefined' to its parents, cause it have yet executes the onload functions.
The output I need is either true/ false. Perhaps the structure of my codes itself is incorrect.
How can I solve this?
Use callback, something like as below:
var validation_callback = function(isValid) {
if (isValid) {
// do something
} else {
alert("wrong size");
}
};
$('input:file').change(function(e) {
ValidateImageSize(this, validation_callback);
});
var ValidateImageSize = function (input, callback) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
var isValid = this.height == 40 && this.width == 40 ? true : false;
callback(isValid);
};
img.src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
};
i want to return imageSize but it is returning undefined. i am confused i have tried many things. in the alert i am getting size.
getImageSize : function(file) {
var reader = new FileReader();
var image = new Image();
var imageSize;
reader.readAsDataURL(file);
reader.onload = function(_file) {
image.src = _file.target.result;
image.onload = function() {
imageSize = ~~(file.size/1024) +'KB';
alert(imageSize);
};
};
return imageSize;
}
Since you are loading the image asynchronously, you cannot return the size directly. The best you can do is pass in a call-back function to be called when the size is available:
getImageSize : function(file, callback) {
var reader = new FileReader();
var image = new Image();
var imageSize;
reader.readAsDataURL(file);
reader.onload = function(_file) {
image.src = _file.target.result;
image.onload = function() {
imageSize = ~~(file.size/1024) +'KB';
callback(imageSize);
};
};
}