Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event onChange won't trigger after files are selected from code-generated INPUT element

I'm playing with JavaScript and wrote simple function that creates INPUT element (type="file") and simulates click.

var createAndCallFileSelect = function () {
    var input = document.createElement ("input");
    input.setAttribute ("type", "file");
    input.addEventListener ("change", function () {
        console.log (this.files);
    }, false);
    input.click();
}

It works great most of time but sometimes it doesn't fire onChange event when file is selected (or more files when used with multiple attribute on INPUT).

I know that onChange won't fire when you re-select same file but clearly this is not the case here. It doesn't fire an event only first time I use this function, and sometimes only. Every next click normally fires onChange if something is selected from dialog.

Already tried searching for this problem here and around but seems that all onChange problems and solutions are related to famous problem with re-selecting same file again.

I found this happens on latest Opera and Firefox, never tested with other browsers. Also. I tried to wait entire page to be loaded but result is still the same - sometimes it doesn't trigger onChange on first call.

Can anyone explain to me why this happens? I already have workaround code, that's not the question, just need explanation of why this happens when INPUT is created and called this way.

Update:

Cascade delay

var function createAndCallFileSelect = function () {
    var input = document.createElement ("input");
    setTimeout (function () { // set type with 1s delay
        input.setAttribute ("type", "file");
        setTimeout (function () {  // attach event with 1s delay
            input.addEventListener ("change", function () {
                console.log (this.files);
            }, false);
            setTimeout (function () { // simulate click with 1s delay
                input.click();
            }, 1000);
        }, 1000);
    }, 1000);
}

This also doesn't work. I tried to delay execution of each line to be sure that everything is executed in right order. 3 seconds after call it opens file-select dialog but again, sometimes it doesn't fire onChange event after file is selected.

like image 489
Wh1T3h4Ck5 Avatar asked Sep 08 '16 20:09

Wh1T3h4Ck5


People also ask

Why is onChange not working?

onchange is not fired when the value of an input is changed. It is only changed when the input's value is changed and then the input is blurred. What you'll need to do is capture the keypress event when fired in the given input. Then you'll test the value of the input against the value before it was keypressed.

What triggers onChange event?

The onchange event occurs when the value of an element has been changed. For radiobuttons and checkboxes, the onchange event occurs when the checked state has been changed.

How do you add onChange to HTML?

To add background color in HTML, use the CSS background-color property. Set it to the color name or code you want and place it inside a style attribute. Then add this style attribute to an HTML element, like a table, heading, div, or span tag.

Can we add onChange in Div?

No; the onchange attribute is only applicable to form field elements ( input , select , textarea , etc). Thanks for your answer.


2 Answers

It's a race condition. It's dependant on what is in the stack and how long certain things might take before the synchronous file browser is called to block the rest of the stack from finishing. With addeventlistener, it's queuing a callback for later use which will be picked up by the event loop when the stack clears. If the stack isn't cleared in time, it won't be called in time. There's no guarantee what will be run when. If you use setTimeout(fn, 0) as Pawel suggested, you'll queue the click() function to be called after the event listener has been placed.

Here's a great video that visualizes everything I'm talking about: https://www.youtube.com/watch?v=8aGhZQkoFbQ

Update: I've noticed something very interesting with chrome after looking into this a bit further...it only allows up to 5 of those elements to be created at once. I did this:

for(var i = 0; i < 20; i += 1) {
    createAndCallFileSelect()
}

with several different numbers in there...and each time, any number greater than 5 only produced 5 input elements with 5 callbacks, while 5 and under produced the correct amount.

I also tried this recursively instead of using the for loop...same results.

Also, the larger the file I select, the longer it takes, but eventually it'll call the callback after it processes the file. This testing so far has all been in chrome.

like image 71
Mike S. Avatar answered Oct 22 '22 08:10

Mike S.


You can do something like this to trigger click on change of dynamically created file input

var input = document.createElement ("input");
input.setAttribute ("type", "file");

input.addEventListener('change', function(){
    input.addEventListener('click', function(){
      alert("Clicked");
      input.removeEventListener("click", function(){})
    }, false);
    input.click();
}, false); 

JS fiddle

I have tested this in chrome, firefox, opera nd IE. It works

like image 42
Siddhartha Chowdhury Avatar answered Oct 22 '22 09:10

Siddhartha Chowdhury