loop ends when function called - javascript

I'm writing what I think is some pretty routine javascript. However I'm having an issue I can't seem to resolve. I'm looping through an array and when it reaches the point of a function being called, the loop breaks. If I take the function out the loops runs to completion. I can't seem to figure it out.
for (i=0;i<response.items.length;i++) {
console.log("cover: ", decodeURIComponent(response.items[i].coverPhotoPath));
previewAlbum(decodeURIComponent(response.items[i].coverPhotoPath));
}
The code for the function being called is...
function previewAlbum(file) {
console.log("preview", file);
var galleryId = "photo";
var gallery = document.getElementById(galleryId);
var img = document.createElement("img");
img.style.cssText = 'width:200px;height:200px;';
img.src = file;
gallery.appendChild(img);
// Using FileReader to display the image content
var reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
Each piece of code works on it's own. But when put together the loop breaks. Any ideas?

Related

JavaScript image.onload generates infinite loop

I've a situation where I need to work on a user-provided image with two different functions.
Get user input
Process the image and put it back processed
note: code is incomplete and shortened for brevity. Please don't point out the irrelevant.
1. Get user input
var fReader = new FileReader();
fReader.onload = function(e){
image = new Image();
image.onload = function(){
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(image, myCallbackToProceed);
};
image.src = e.target.result;
};
fReader.readAsDataURL(src);
2. Process the image and put it back processed
function processOnCanvasAndBack(image) {
var canvas = $('<canvas></canvas>');
canvas.draw(image);
canvas.doSomeStuffLikeRotatingAndColorBalance();
//BEGIN OF RELEVANT SECTION
image.onload = function() {
myCallbackToProceed();
};
image.src = canvas.toDataURL();
}
Problem
The image.onload from 1. calls the function as expected but when I call the second image.src from 2 the first image.onload gets called again, which in turns calls 2 again and.... booooom, infinite loop (console spits too many recursions)
I tried to reset the first call with image.onload = function(){}; in various points, but it doesn't fix the issue (no more recursion, but the functions just stop being called). Right now I'm out of ideas :-(
I would suggest creating two image objects. One for the source image, and a second for the transformed / target image. You avoid mutating existing state and causing infinite loops by repeatedly setting the .src of the same image object in the onload events. Avoid mutating state whenever possible. I would also suggest using the var keyword to define the variables locally instead of in the global scope.
var fReader = new FileReader();
fReader.onload = function(e){
var sourceImage = new Image();
var targetImage = new Image();
sourceImage.onload = function() {
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(sourceImage, targetImage, myCallbackToProceed);
}
sourceImage.src = e.target.result;
};
fReader.readAsDataURL(src);
function processOnCanvasAndBack(sourceImage, targetImage, callback) {
var canvas = $('<canvas></canvas>');
canvas.draw(sourceImage);
canvas.doSomeStuffLikeRotatingAndColorBalance();
// BEGIN OF RELEVANT SECTION
targetImage.onload = function() {
callback();
};
targetImage.src = canvas.toDataURL();
}

get the result from the FileReader()

I have the following problem.
I have an upload form for multiple files. The upload process goes fine. The problem is related with the FileReader() result. As I am uploading multiple files(images) I need to create corresponding thumbs when the upload is finished.
files = e.target.files;
for (var i = 0; i < files.length; i++) {
//ajax request goes here
var reader = new FileReader();
reader.onload = (function (e) {
var img = document.createElement('img');
img.file = e;
img.className = 'thumbs_';
img.src = e.target.result;
//Every image has a wrapper div with the id 'nDv0','nDv1','nDv2' etc.
document.getElementById('nDv' + i).appendChild(img);
})(e);
reader.readAsDataURL(files[i]);
//request sent
}
If I remove the closures around the anonymous function, i's value will be anything when the for loop exits.
For example, if there are 3 files, i's value will be 3 and the results will be appended to the last wrapper div and images will be displayed.
With closures every image is getting appended to the corresponding div, but the images won't be displayed as the returned result is undefined.
So, how can I append every thumb to its corresponding div?
You has a logic error:
reader.onload = (function (e) {
//onload code
//inside function e is event from outside
})(e);//e is event from upper code
And the i will be equal to files.length because onload handler is async.
So you must change your onload handler to:
reader.onload = function (index) {
var img = document.createElement('img');
img.className = 'thumbs_';//it's right className?
img.src = this.result;//result is dataURL
document.getElementById('nDv' + index).appendChild(img);
}.bind(reader, i);

Image in localstorage function doesn't work

I'm wrting a function which takes an image from a file input from a form and enables me to put it in localstorage. The function I wrote to achieve this:
function getImage() {
var pic = document.getElementById("image").files[0];
var imgUrl;
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
return imgUrl;
}
}
Then in another function I call this function and create a JSON entry in which I store values from other form inputs including the image. It looks like this:
var imgUrl = getImage();
// Create new JSON entry
var json_entry = {'title': titleField.val(),
'image': imgUrl,
'content': contentField.val(),
'location': location};
Sadly the value of imgUrl is undefined.. There are no console errors. What am I doing wrong? And how can I fix this?
I honestly don't know much about the FileReader object, but I can see just from glancing at your JS that (at least) one thing is off:
var imgUrl = getImage();
Your getImage function doesn't return anything; so imgUrl is definitely going to be undefined above.
If you want to do something with the result property of your FileReader, then you need to do so w/ a callback since you're handling the (asynchronous) onload event:
function getImage(callback) {
// What are you doing with this?
var pic = document.getElementById("image").files[0];
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
// Note the difference here: rather than return from the event handler
// (which effectively does nothing) we pass the result to a callback.
callback(imgUrl);
}
// I assume you actually need to load something with the FileReader?
}
And then:
getImage(function(imgUrl) {
var json_entry = {
'title': titleField.val(),
'image': imgUrl,
'content': contentField.val(),
'location': location
};
});
It looks like you are forgetting to set the reader to readAsDataUrl. Likely the value is coming back as undefined because localStorage does not inherently know how to serialize binary data. Setting the reader to readAsDataUrl changes reader.result onload.
var reader = new FileReader();
reader.onload = function(e) {
var imgURL = reader.result;
saveDataToLocalStorage(imgURL);
callback(imgUrl);
};
// add this line
reader.readAsDataURL(pic);
Have a look at this article, especially the section titled Reading Files. Note in the linked example the author uses e.target.result instead of reader.result. This should be the same value.

Passing file contents to outside variable

I'm using a FileReader and the HTML file dialog to read a file in my script. How do I pass this file's contents out of the FileReader.onload function?
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener
('change', readFileData, false);
/* I want to access the contents here */
I tried sticking returns in the readFileData and onload functions, but I'm not sure what they return to.
I assume that you know, its async and all.
So, the short answer is: No, you can not do that.
However, if you want the contents to be globally accessible for any future calls, you could something like this:-
var contents;// declared `contents` outside
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result; //<-- I removed the `var` keyword
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener('change', readFileData, false);
var reasonableTimeToWaitForFileToLoad = 100000;
console.log(contents); //`contents` access first attempt: prints undefined
setTimeout(function() {
console.log(contents);//`contents` access second attempt: prints the contents 'may be if the time allows.'
}, reasonableTimeToWaitForFileToLoad);
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
reader.onloadend=function(e) {
console.log(contents)
}
}
document.getElementById('file').addEventListener('change', readFileData, false);
This is a scoping issue. When you're declaring contents within the onload, it's no longer available after that function has run. You need to declare contents outside of that scope first.
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
}
document.getElementById('file').addEventListener
('change', readFileData, false);
//make changes here
//contents should have the correct value
Firstly, you have to realize that reading a file with a FileReader is an asynchronous task, and you cannot work with it in a synchronous manner.
There are many ways to handle this, but many of them are not suited for recommendations ;)
I would do it one of these 2 ways:
1: you can call a function from within the onload event handler and pass the file contents as a parameter
2: you can trigger an event from within the onload event handler and pass the file contents as event data
Just declare contents outside both functions and assign to it inside the inner function:
var contents;
var outer_fn = function() {
var inner_fn = function() {
contents = '42';
}
inner_fn();
}
outer_fn();
// here, `contents' is '42'
I faced a similar challenge and this is what I used to solve the issue.
var contents;
function readFileData(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
contents = e.target.result;
}
reader.readAsText(file);
//calling to access the 'contents' variable
accessFileContents();
}
document.getElementById('file').addEventListener('change', readFileData, false);
var wait4file2load = 1000;
/* To access 'contents' here */
function accessFileContents(){
setTimeout(function(){
console.log(contents);
}, wait4file2load);
}
It won't give undefined value since we are calling it after the file is completely uploaded.
I had a similar problem in Angular 7 (typescript), and this is how I solved my problem.
What I wanted to do was to access the base64 conversion that was happening inside fileReader -> reader.onload
Then pass that parameter to another method where I could convert it to a JSON object then post it to the API seeing I want to post another parameter as well in the post. (not added in this code)
What I did first was to declare what I potentially needed to access outside the Method that.
base: any = null;
base64File: any = null;
fileToTest: any = null;
Then I converted the pdf to base64 when the upload event fired
convertToBase64 (e){
this.fileToTest = e.target.files[0];
var reader = new FileReader();
reader.onload = () => {
this.base64File = reader.result.slice(28);
};
reader.onerror = function (error) {
console.log('Error: ', error);
}.bind(this.base64File);
reader.readAsDataURL(this.fileToTest);
return this.base64File;
}
Finally access the base64 file in the other method
onSubmit() {
console.log("base 64 file is visible", this.base64File);
var base =
{
"FileBase64": this.base64File,
"Path": "document",
"FileType": ".pdf"
};
console.log("JSON object visible", base);
this.uploadService.base64Post(base);
}
Everything works now, and hopefully maybe this can help someone else finding themselves with the same problem.
Using Angular 7, code is in the component file, and the post function is in the Service file. Without my comments the code is exactly like this in the component file.

jQuery variable not being updated

I am using HTML5 file API to get the width of an image.
Its working, and the console.log is outputting the correct amount.
I need to save the value to the variable fileWidth so I can access it outside the function. I have created an empty variable outside the function, and expected it to be updated with the alue inside the function, but whatever I try fails.
Here is my code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.onload = function() {
fileWidth = img.width;
console.log(fileWidth);
};
img.src = reader.result;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
Can anyone see why the variable isn't being updated?
Edited Code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.src = reader.result;
};
reader.onloadend = function() {
fileWidth = img.width;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
console.log(fileWidth);
You're setting fileWidth in an asynchronous callback, this method isn't guaranteed to have executed prior to your accessing the variable.
Hint
Try putting an alert in your callback and an alert right before you access the variable. See which alert is presented first.

Categories

Resources