14

I've made a small script using some of the HTML5 files features, which allows you to select one or more files, and each time it will write the name of the file(s). Everything works as it should, only the event to detect the value change of the files input fire only once, so how can I make it fire every change and not only on the first change?

By the way, here is what I made: http://tamir.netspot.co.il/html5/files/

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Cokegod
  • 8,256
  • 10
  • 29
  • 47
  • [http://stackoverflow.com/questions/9155136/chrome-file-upload-bug-on-change-event-wont-be-executed-twice-with-the-same-fi/11280864#11280864][1] [1]: http://stackoverflow.com/questions/9155136/chrome-file-upload-bug-on-change-event-wont-be-executed-twice-with-the-same-fi/11280864#11280864 – fundon Jul 01 '12 at 09:30
  • Please read my comment below. Onchange only fires the first time because you have to clear the value of the previous file selection. – edgarhsanchez Feb 24 '17 at 04:57

10 Answers10

43

If you want to upload twice, clear file input value

$('input[type="file"]').val(null);

jsfiddle test

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
fundon
  • 1,039
  • 2
  • 9
  • 6
12

It appears that the change event listener is being removed because you're using innerHTML to update the same element (wrapper) that the input itself is inside. So the contents of the wrapper element – including the file input – is being re-rendered, and along the way, the event listener is removed (or, rather, it's connected to an element that's no longer there).

Here's a simple jsfiddle that does exactly the same as your code, except that it prints the selected file names in a different element than the element the input is in. And it works (in WebKit, anyway)

Here's further proof (I basically copied your code, and only added a line to re-register the event listener after the modification of wrapper.innerHTML)

So, the change event does fire for each change, but the input that's being observed is removed by the use of innerHTML on the input's parent element.


I honestly don't know whether this is a legitimate browser bug or not. It makes sense for innerHTML to "overwrite" the existing input element, yet the browser is smart enough to not not reset the input's value, so you'd think listeners would stick around too… so… well, it's confusing

Flambino
  • 18,507
  • 2
  • 39
  • 58
  • Whether this is a legitimate browser bug or not, thank you for finding where my problem comes from. – Cokegod Aug 24 '11 at 23:28
7

I'm not sure why but none of the answers to this old question are all that simple. Here's the way to do this easily today...

with jquery...

$('#myfileinputfieldid')[0].onchange = function(e) {
 //do something with e.  Like write an image to a canvas or make a yummy cup of coffee
  e.target.value = '';
};

that's it after you have changed the value to something other than the file that was selected the next time the file input is clicked the onchange event will fire.

edgarhsanchez
  • 1,433
  • 2
  • 12
  • 9
2

Basically, if you still have a value for your input, no extra event would be fired. I'm working with react and i Had to clear the value of the input for the next event to be triggered. Using a ref, you can do something like this.

 buttonRef.current.value = null;
codemon
  • 62
  • 4
1

Instead of using onchange use oninput event

 $scope.ShowIcon = function (input) {
        if (input.files && input.files[0]) {
            var reader = new FileReader();
            reader.onload = function (e) {
                $('#iAIcon')
                    .attr('src', e.target.result)
            };
            reader.readAsDataURL(input.files[0]);           
        }        
    }
user3082420
  • 11
  • 1
  • 2
0

None of the above worked for me, I actually had to create a new "dummy" file input field each time it was changed - allowing me to capture the change event again.

The process for me was:

OnChange - move file input to another element - create a new file input to capture the change event again

Phil LaNasa
  • 2,907
  • 1
  • 25
  • 16
0

addEventListener wont work for IE8(Not sure about IE9 onwards). We need to use attachEvent listiner. If you need cross browser support then use this

if (!inputfile.addEventListener) {
    inputfile.attachEvent("onclick", setCheckedValues); //IE8
}else {
    inputfile.addEventListener("click", setCheckedValues, false); //Other browser
}
Chetan Nellekeri
  • 300
  • 2
  • 16
0

ok well according to @Flambino the input is being re-rendered. For whatever reason this may be, for me its irrelevant. The $.on('change', callback) functionality is lost.

Try using .delegate function which I absolutely love! http://api.jquery.com/delegate/

Ok so delegate is exactly the same, it just tells jquery if there is an element rendered on screen with a particular handle, attach a functionality to it.

So even if the element is re-rendered, it will still keep to function.

$(document).delegate('.file_upload_btn', 'change', function(){});

You may think this is a throw away function & say whats the difference but this has saved me a lot of time on projects.

A H Bensiali
  • 825
  • 1
  • 9
  • 22
0

I got the .change callback to fire on every new file by reassigning the .change function at the end of its own callback:

$('#myfileinputfieldid').change(function (event) {
    scope.processFile(event.target.files[0]);
});

scope.processFile = function(fileStruct) {
    doStuff;

    // Reassign the onchange callback.
    $('#myfileinputfieldid').change(function (event) {
        scope.processFile(event.target.files[0]);
    });
};
Kevin J
  • 23
  • 1
  • 5
0

In my case i use ajax to upload file. I just clear the value of input with onclick event handler.

  $('#myFile').click(function(e) {e.target.value = '';});
  $('#myFile').change(function(e) { 
           var file = e.target.value;
           var formdata = new FormData();
           formdata.append('file', file, 'somefile');
           $.ajax({
             type: 'POST',
             url: './uploadFile',
             data: formdata,
             processData: false,
             contentType: false,
             success: function(data){

             }
        });
  });