Программно сгенерированный /активированный ввод файла не всегда запускает событие `input`
У меня есть кнопка в моем веб-приложении, в коде которой есть обработчик события click:
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'));
Иногда (примерно 1 из 8) после выбора файла событие input
не происходит t после выбора файла. Я предполагаю, что это ошибка браузера в жизненном цикле элемента.
Есть ли способ добавить элемент на страницу и удалить его позже? Какой правильный способ справиться с этим в современных браузерах в наши дни?
Я тестирую с Google Chrome в Windows.
JSFiddle: http://jsfiddle.net/pja1d5om/2/
2 ответа
Цитата из вашего вопроса: Иногда (примерно 1 из 8) после выбора файла событие ввода не срабатывает после выбора файла.
Я могу подтвердить это поведение с помощью input
и с помощью change
события с использованием Opera (версия 55.0.2994.61, самая последняя на данный момент версия), которая использует Движок браузера Google Chrome" Blink ". Это происходит примерно в 1 из 25 случаев.
Решение
Это происходит потому, что иногда ваш объект элемента ввода удалялся после закрытия диалогового окна файла, потому что он больше не используется. И когда это происходит, у вас нет цели, которая могла бы получить input
или change
событие.
Чтобы решить эту проблему, просто добавьте свой элемент ввода где-нибудь в DOM после создания как скрытого объекта следующим образом:
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
А затем, когда событие было запущено, вы можете удалить его следующим образом:
document.body.removeChild(fileInputEl);
Полный пример
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://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
<input type="button" onclick="selectFile()" value="Select file">
Цитата из описания вашей награды: Баунти будет присуждаться тому, кто ... покажет подходящий обходной путь.
Мой старый предложенный обходной путь (теперь не имеет значения)
Мы можем использовать функцию setInterval
, чтобы проверить, изменилось ли входное значение. Мы сохраняем intervalID
в нашем новом fileInputEl
как имущество. Поскольку мы всегда создаем новый элемент ввода файла, его значение всегда пусто при запуске (при каждом нажатии кнопки). И если это значение было изменено, мы можем обнаружить его, сравнивая с пустой строкой. И когда это происходит, мы передаем fileInputEl
в fileInputChanged()
и очистите /остановите нашу функцию интервала.
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://docs.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">
Это очень интересная ошибка, которую вы там обнаружили, я не смог ее воспроизвести.
Есть ли причина, по которой вы подходите к вводу файла так, как делаете? Это потому, что вы хотите попробовать и стилизовать это?
Я прочитал эту статью и я применил то, что делал. Я обнаружил, что это работает нормально. В этой статье мы попытаемся подключить метку к входу с атрибутом for на метке метки. Затем в CSS и JavaScript тег ввода файла скрыт, а метка по сути действует как «кнопка».
Например ...
Примечание Я действительно внес некоторые изменения в код, но все это заслуга Освалдаса Валутиса, автора статьи, упомянутой выше в CoDrops.
var inputs = document.querySelectorAll('.inputfile');
inputs.forEach(input => {
var label = input.nextElementSibling,
labelVal = label.innerHTML;
input.addEventListener('change', function(e) {
var fileName = '';
if (this.files && this.files.length > 1)
fileName = (this.getAttribute('data-multiple-caption') || '').replace('{count}', this.files.length);
else
fileName = e.target.value.split('\\').pop();
if (fileName)
label.querySelector('span').innerHTML = fileName;
else
label.innerHTML = labelVal;
});
});
* {
font-family: sans-serif;
font-weight: 300;
}
.inputfile {
display: none;
}
.inputfile+label {
font-size: 1.25em;
font-weight: 700;
color: white;
background-color: darkred;
display: inline-block;
padding: 10px;
border-radius: 10px;
border: 1px darkred solid;
cursor: pointer;
}
.inputfile+label:hover {
background-color: darkred;
}
<input type="file" name="file" id="file" class="inputfile" data-multiple-caption="{count} files selected" multiple />
<label for="file">Choose a file <span></span></label>
Теперь я знаю, что это может быть не совсем то, что вы ищете, но это может быть альтернативным решением, которое вы можете попробовать.