I have a button on my web application, which has the following code in the click event handler:
const fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
fileInputEl.addEventListener('input', (e) => {
if (!e.target.files.length) {
return;
}
// Handle files here...
});
fileInputEl.dispatchEvent(new MouseEvent('click'));
Sometimes (about 1 out of 8), after selecting the file, the input
event doesn't fire after choosing a file. I'm guessing this is a browser bug around the lifecycle of the element.
Any way around this short of appending the element to the page and removing it later? What's the proper way to handle this in modern browsers these days?
I'm testing with Google Chrome on Windows.
JSFiddle: http://jsfiddle.net/pja1d5om/2/
Citate from your question: Sometimes (about 1 out of 8), after selecting the file, the input event doesn't fire after choosing a file.
I can confirm this behavior with input
and with change
events using Opera (ver. 55.0.2994.61, newest version at this time) which uses Google Chrome browser engine "Blink". It happens about 1 out of 25.
This happens because sometimes your input element object was deleted after file dialog closing because it is not in using anymore. And when it happens you have not the target which could receive input
or change
event.
To solve this just add your input element somewhere to the DOM after creating as hidden object like follows:
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
And then when the event was fired you can delete it like follows:
document.body.removeChild(fileInputEl);
Full example
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
fileInputEl.addEventListener('input', function(e)
{
// Handle files here...
console.log('You have selected ' + fileInputEl.files.length + ' file(s).');
document.body.removeChild(fileInputEl);
});
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
<input type="button" onclick="selectFile()" value="Select file">
Citate from your bounty description: Bounty will be awarded to someone who ... show an appropriate workaround.
My old suggested workaround (now irrelevant)
We can use setInterval
function to check if input value was changed. We save intervalID
in our new fileInputEl
as property. Because we always create a new file input element then its value is always empty on start (on each button click). And if this value was changed we can detect it when we compare it with empty string. And when it happens then we pass our fileInputEl
to fileInputChanged()
function and clear/stop our interval function.
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.intervalID = setInterval(function()
{
// because we always create a new file input element then
// its value is always empty, but if not then it was changed:
if(fileInputEl.value != '')
fileInputChanged(fileInputEl);
}, 100);
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
function fileInputChanged(obj)
{
// Handle files here...
console.log('You have selected ' + obj.files.length + ' file(s).');
clearInterval(obj.intervalID);
}
<input type="button" onclick="selectFile()" value="Select file">
It seems this is a browser bug/fluke and likely has something to do with garbage collection. I can get around it by adding the file input to the document:
fileInputEl.style.display = 'none';
document.querySelector('body').appendChild(fileInputEl);
When done, it can be cleaned up with:
fileInputEl.remove();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With