FileReader - Preparing for new file, even the same one - javascript

I have several load file html elements, eg:
<input type="file" id="loadFiles0" class="filesclass" name="file" />
<input type="file" id="loadFiles1" class="filesclass" name="file" />
I have added an event listener to them to catch changes:
// Elsewhere
function myFunction(event){
// Stuff
}
var el;
el = document.getElementById("loadFiles0");
el.addEventListener('change', myFunction, false);
As many will know, to get the load working the second time, EVEN WHEN IT IS THE SAME FILE NAME, you must set the html element's 'value' to "". This is the problem. I need to know which one of the load file elements did the call. Was it 'loadFiles0' or 'loadFiles1' etc.
myFunction looks like this - just the important bits:
function myFunction(evt){
...
// We need to remove it so this is not handled again when we set value = ""
this.removeEventListener('change', myFunction, false);
...
var reader = new FileReader();
reader.onload = function (e) {
...
// HERE IS THE PROBLEM
// I need a reference to the dom element that this is working on - call it 'ptr'
// I cannot use 'this' in this function, because that refers to 'reader'
// I need it so I can set its value to "", so even if the person reloads the same file, it will trigger a change
// But I cannot be certain if it was 'loadFiles0' or 'loadFiles1' etc
ptr.value = "";
ptr.addEventListener('change', myFunction, false);
};
}
So the question is, how can I get ptr in the reader's onload function?

I need to know which one of the load file elements did the call. Was it 'loadFiles0' or 'loadFiles1' etc.
It will be this within the event callback to myFunction, which you can then either remember in a variable (ptr, perhaps), or if you want to use ES2015 (with transpiling if necessary) you can use an arrow function.
With ptr:
function myFunction(evt){
// ...
this.removeEventListener('change', myFunction, false);
var ptr = this; // *******
// ...
var reader = new FileReader();
reader.onload = function (e) {
ptr.value = "";
ptr.addEventListener('change', myFunction, false);
};
}
Or with an ES2015+ arrow function:
function myFunction(evt){
// ...
this.removeEventListener('change', myFunction, false);
// ...
var reader = new FileReader();
reader.onload = e => { // ***
this.value = ""; // ***
this.addEventListener('change', myFunction, false); // ***
};
}
Example using setTimeout to emulate the reader.onload callback:
function myFunction(e) {
var ptr = this;
// This emulates the reader.onload callback:
setTimeout(function() {
console.log("reader.onload for " + ptr.id);
}, 10);
}
Array.prototype.forEach.call(
document.querySelectorAll("input[type=file]"),
function(input) {
input.addEventListener("change", myFunction, false);
}
);
<input type="file" id="loadFiles0">
<input type="file" id="loadFiles1">
<input type="file" id="loadFiles2">
Example with an arrow function:
// ES2015 or higher (you can transpile if necessary)
function myFunction(e) {
// This emulates the reader.onload callback:
setTimeout(() => {
console.log("reader.onload for " + this.id);
}, 10);
}
Array.prototype.forEach.call(
document.querySelectorAll("input[type=file]"),
function(input) {
input.addEventListener("change", myFunction, false);
}
);
<input type="file" id="loadFiles0">
<input type="file" id="loadFiles1">
<input type="file" id="loadFiles2">

Related

Javascript file in conflict with other javascript

I think my javascript in my php file is in conflict with my javascript file.
I have a script that checks of the image is smaller then 2MB and a script that shows the image you selected in a small version. But the second part does not work when the first script is active. how do I fix this?
script in HTML
<script>
window.onload = function() {
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
if(this.files[0].size > 2000000){
alert("File is too big!");
this.value = "";
};
};
var uploadField = document.getElementById("itemImages");
uploadField.onchange = function() {
if(this.files[0].size > 200){
alert("File is too big!");
this.value = "";
};
};
}
</script>
.js file
$("#frontImages").change(function () {
if ($('#frontImages').get(0).files.length > 0) {
$('#frontImages').css('background-color', '#5cb85c');
} else {
$('#frontImages').css('background-color', '#d9534f');
}
});
$("#itemImages").change(function () {
if ($('#itemImages').get(0).files.length > 0) {
$('#itemImages').css('background-color', '#5cb85c');
} else {
$('#itemImages').css('background-color', '#d9534f');
}
});
document.getElementById("frontImages").onchange = function () {
var x = document.getElementById('previewFrontImage');
x.style.display = 'block';
var reader = new FileReader();
reader.onload = function (e) {
document.getElementById("previewFrontImage").src = e.target.result;
};
reader.readAsDataURL(this.files[0]);
};
function previewImages() {
var $preview = $('#previewItemImages').empty();
if (this.files) $.each(this.files, readAndPreview);
function readAndPreview(i, file) {
var reader = new FileReader();
$(reader).on("load", function () {
$preview.append($("<img/>", {src: this.result, height: 100}));
});
reader.readAsDataURL(file);
}
}
$('#itemImages').on("change", previewImages);
I'm guessing that the conflict is between the html script and this
document.getElementById("frontImages").onchange = function ()
I also have a question how I can fix that there will be no small image when the image is too big
Your guess is correct, onchange is simply member variable of various elements, and thus
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
and
document.getElementById("frontImages").onchange = function ()
are setting this single variable (of frontImages), which will store one callback function at a time.
You could use addEventListener() instead, which maintains a list of event listeners, so there can be more than one. Modifying the lines to
var uploadField = document.getElementById("frontImages");
uploadField.addEventListener("change", function() {
and
document.getElementById("frontImages").addEventListener("change", function ()
will register both event listeners on frontImages, regardless of the order they are executed.
Side remark: when you have "nice" ids, document.getElementById() can be omitted, as elements with ids become variables (of window which is the global scope), and thus you could write frontImages.addEventListener(...). You still need the getter in various cases, like when a local variable shadows the id, or when it is not usable as variable identifier (like id="my-favourite-id" or id="Hello World")

knockout change event function

Trying to upload multiple csv files using a knockout change event. Initially the change function works and the values get written to the viewmodel. The problem is when trying to upload a second csv file the knockout change event doesn't fire. Is there a way to get the change function to re-fire after the first time?
<input id="uploadFile" type="file" multiple="multiple" data-bind="event: { change: PO.fileUploadChange }"/>
export function fileUploadChange(data, evt): void {
ko.utils.arrayForEach(evt.target.files, function (file) {
var reader = new FileReader();
reader.onload = LoadCSVData;
reader.readAsText(evt.target.files.item(0))
model.quickEntryModel.files.push(evt.target.files.item(0));
var input = document.getElementById('uploadFile');
if (input != null)
document.getElementById('uploadFile').outerHTML = input.outerHTML;
})
}
The issue is with the last line. I'm not sure what you're trying to do there, but I'm assuming you're clearing the input. You should move it outside the arrayForEach loop:
export function fileUploadChange(data, evt): void {
ko.utils.arrayForEach(evt.target.files, function (file) {
var reader = new FileReader();
reader.onload = LoadCSVData;
reader.readAsText(evt.target.files.item(0))
model.quickEntryModel.files.push(evt.target.files.item(0));
});
var input = document.getElementById('uploadFile');
if (input != null)
input.value = "";
}
Here's a fiddle

How to pass event data and id to javascript function

Im using javascript to be able to display an image pre file upload but Im having this error...
I have a javascript function
function imageIsLoaded(e) {
$('#image_preview_new').css("display", "block");
var previewDiv = $('#previewing_new');
previewDiv.attr('src', e.target.result);
}
and I am calling the function via another script, but I need to be able to pass both the event data as well as an id into that function but it doesn't seem to work...
This is what I am currently using and it works...
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(this.files[0]);
This is what I've been trying and it doesn't work. If try and pass the id and the event data (e, id) it tells me that "e is not defined"...
var reader = new FileReader();
reader.onload = imageIsLoaded(e, id);
reader.readAsDataURL(this.files[0]);
function imageIsLoaded(e, id) {
$('#image_preview_new').css("display", "block");
var previewDiv = $('#previewing_new');
previewDiv.attr('src', e.target.result);
}
How can I get the event data as well as an id into this function.
Thanks.
Use .change() event, Function.prototype.bind()
function imageIsLoaded(id, e) {
// `id` : `"abc"` , `e` : `event`
console.log(id, e);
// do stuff with `id`
var previewDiv = $("#" + id);
previewDiv.attr("src", e.target.result);
}
$(":file").change(function() {
var reader = new FileReader();
// set `this` to `reader` at first parameter to `.bind()`
// pass `id` : `"abc"` at second parameter `.bind()` ,
// called at `imageIsLoaded`
reader.onload = imageIsLoaded.bind(reader, "abc");
reader.readAsDataURL(this.files[0]);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<input type="file">
<br />
<img id="abc" />

getElementsByName('q')[0] returns undefined

I'm trying to get a textbox to enlarge when focused
The relevant html code is
<input class="tbox" name="q" type="text" />
Since tbox is a catchall for every textbox in the page, I have to work with the name.
I tried this javascript
window.onload = init();
function init()
{
var arr = new Array();
arr = document.getElementsByName("q");
var querybox = arr[0];
querybox.addEventListener('onfocus', wasclicked, false);
querybox.addEventListener('onblur', lostfocus, false);
}
function wasclicked(form)
{
form.q.style['width'] = '500px';
}
function lostfocus(form)
{
form.q.style['width'] = '276px';
}
Debug console tells me I can't use addEventListener on an undefined.
I'm not familiar with javascript, so I tried [0].value as well to no avail.
There are several different issues. First off, you need to use:
window.onload = init; // assign function reference to .onload to call later
instead of:
window.onload = init(); // calls init() immediately and assigns return value to .onload
This is a very common JS mistake. The way you have it, you are calling init() immediately (before the page has loaded) and assigning its return value to window.onload which won't work.
You want to assign a function reference to window.onload. A function reference is the name of the function WITHOUT any parens after it. Adding parens instructs the JS interpreter to execute the function NOW. Assigning just the name puts a pointer to the function into window.onload so it can be called later when the onload event fires.
You also have to fix your event handler callbacks because the form is not what is passed to them. The argument to an event handler is the Event object. The object that caused the event will be in this or event.target:
function wasclicked(e) {
this.style.width = '500px';
}
function lostfocus(e) {
this.style.width = '276px';
}
And, the event names are "blur" and "focus" so you need to change this:
querybox.addEventListener('onfocus', wasclicked, false);
querybox.addEventListener('onblur', lostfocus, flase);
to this:
querybox.addEventListener('focus', wasclicked, false);
querybox.addEventListener('blur', lostfocus, flase);
Combining everything and doing a little simplification, the whole thing should look like this:
window.onload = init;
function init() {
var querybox = document.getElementsByName("q")[0];
querybox.addEventListener('focus', wasclicked, false);
querybox.addEventListener('blur', lostfocus, false);
}
function wasclicked(e) {
this.style.width = '500px';
}
function lostfocus(e) {
this.style.width = '276px';
}
Note that onfocus is the the event handler, but the event itself is focus.
function init()
{
arr = document.getElementsByName("q");
var querybox = arr[0];
querybox.addEventListener('focus', wasclicked, false);
querybox.addEventListener('blur', lostfocus, false);
}
function wasclicked(e)
{
e.target.style.width = '500px';
}
function lostfocus(e)
{
e.target.style.width = '276px';
}
window.onload = init();
As you can see, the event itself contains the elemet so there's no need to add any other variable.
Just know that addEventHandler won't work on all browsers.
Try this:
window.onload = init();
function init()
{
var arr = document.getElementsByName("q");
var querybox = arr[0];
querybox.addEventListener('onfocus', wasclicked, false);
querybox.addEventListener('onblur', lostfocus, flase);
}
function wasclicked(form)
{
form.q.style['width'] = '500px';
}
function lostfocus(form)
{
form.q.style['width'] = '276px';
}

Getting the dimensions of a newly updated image element

I'm having an issue with getting the dimensions of a newly updated image element.
I'm using FileReader to display a preview of an image file on screen.
Once the filereader and associated code has finished executing, I retrieve the new dimensions of the image element so I can adjust them accordingly.
On some browsers, the info doesn't get updated straight away and I need to include a delay before I can retrieve the new dimensions. This results in the new image being resized according to the previous image's dimensions, and not its own.
How can I be sure the new image has been fully loaded?
I would have thought FileReader().onloadend would have been sufficient but apparently not. I think this is executed once the fileReader has loaded it, not when the DOM has rendered it. (Am I right?)
Then I found this question, which suggests using a timeout of 1 millisecond. This works sometimes, but not always. It seems to be down to the speed the browser is rendering the image (more-so available resources rather than filesize). Do we have no other way of detecting a change in these parameters?
HTML
<img src="#.png" alt=""/>
<input id="fileInput" type="file" accept='image/*' onChange="loadIt()" />
JS
preview = getElementsByTagName('img')[0];
function loadIt() {
var imgReader = new FileReader();
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
imgReader.onloadstart = function(e) {
//
}
imgReader.onload = function (imgReaderEvent) {
if (isImage()) {
preview.src = imgReaderEvent.target.result;
//
}
else {
preview.src = 'error.png';
//
}
}
imgReader.onloadend = function(e) {
setImage();
setTimeout(function(){
setImage();
}, 1);
setTimeout(function(){
setImage();
}, 2000);
//
}
};
function setImage() {
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
//
};
You should call preview.onload or preview.onloadend on your image to detect that it has finished loading. You're also calling your events after you readAsDataURL The code should look like this
var preview=document.getElementsByTagName("img")[0];
function loadIt() {
var imgReader=new FileReader();
imgReader.onload=function(){
if (isImage()) {
preview.src = imgReader.result;
preview.onload=setImage;
} else {
preview.src = 'error.png';
//
}
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
}
function setImage(){
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
}
You're calling imgReader.readAsDataURL before your .onload and .onloadend is set.
I do not know what some of the variables are with-in the body of your setImage();however, start here:
function loadIt() {
var preview = getElementsByTagName('img')[0],
setImage = function () {
preview_w = preview.width;
preview_h = preview.height;
console.log('dimensions: '+avatar_preview_w+' x '+avatar_preview_h);
},
imgReader = new FileReader();
imgReader.onloadstart = function(e) {};
imgReader.onload = function (imgReaderEvent) {
if (isImage()) {
preview.src = imgReaderEvent.target.result;
}
else {
preview.src = 'error.png';
};
};
imgReader.onloadend = function(e) {setImage();};
imgReader.readAsDataURL(document.getElementById('fileInput').files[0]); //read from file input
};

Categories