Событие onChange не запускается после выбора файлов из созданного кодом элемента ввода

Я играю с JavaScript и написал простую функцию, которая создает INPUT элемент (type="file") и имитирует клик.

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

он отлично работает большую часть времени, но иногда он не срабатывает onChange событие при выборе файла (или более файлов при использовании с multiple атрибут on INPUT).

Я знаю, что onChange не будет срабатывать при повторном выборе того же файла, но, очевидно, это не так. Он не запускает событие только в первый раз, когда я использую эту функцию, и только иногда. Каждый следующий щелчок обычно срабатывает onChange если что-то выбирается из диалогового окна.

уже пробовали искать эту проблему здесь и вокруг, но, кажется, что все onChange проблемы и решения связаны с известной проблемой с повторным выбором того же файла снова.

я обнаружил, что это происходит на последних Opera и Firefox, никогда не тестировался с другими браузерами. Также. Я попытался дождаться загрузки всей страницы, но результат все тот же - иногда это не срабатывает onChange по первому вызову.

кто-нибудь может объяснить мне, почему это происходит? у меня уже есть решение код, это не вопрос, просто нужно объяснить, почему это происходит, когда ввод создается и вызывается таким образом.

обновление:

Каскад задержки

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);
}

это также не работает. Я попытался отложить выполнение каждой строки, чтобы убедиться, что все выполнено в правильном порядке. 3 секунды после вызов открывает диалог выбора файла, но опять же, иногда он не срабатывает onChange событие после выбора файла.

4 ответов


это условие гонки. Это зависит от того, что находится в стеке и сколько времени может потребоваться, прежде чем синхронный файловый браузер будет вызван, чтобы заблокировать остальную часть стека от завершения. С addeventlistener это очередь обратного вызова для последующего использования, который будет подхвачен циклом событий, когда стек очищается. Если стек не будет очищен вовремя, он не будет вызван вовремя. Нет никакой гарантии, что будет работать, когда. Если вы используете setTimeout (fn, 0), как предложил Павел, Вы очередь функция click (), вызываемая после размещения прослушивателя событий.

вот отличное видео, которое визуализирует все, о чем я говорю:https://www.youtube.com/watch?v=8aGhZQkoFbQ

обновление: Я заметил что-то очень интересное с chrome после изучения этого немного дальше...он позволяет создавать только до 5 из этих элементов сразу. Я сделал это:

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

с несколькими различными номерами в там...и каждый раз любое число больше 5 производило только 5 входных элементов с 5 обратными вызовами, в то время как 5 и ниже производили правильное количество.

Я также попробовал это рекурсивно вместо использования цикла for...те же результаты.

кроме того, чем больше файл, который я выбираю, тем дольше он занимает, но в конечном итоге он вызовет обратный вызов после обработки файла. Эти испытания до сих пор все было в хроме.


вы можете сделать что-то вроде этого, чтобы вызвать щелчок по изменению динамически созданного ввода файла

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

я протестировал это в chrome, firefox, opera ND IE. Это работает


прослушивателя событий input.addEventListener ("change" ...

не регистрируется сразу. Это как обернуть код в setTimeout(fn, 0) что делает его добавленным в конец очереди выполнения.

но ввод.нажмите (); мгновенно открывает всплывающее окно выбора файла, которое приостанавливает JavaScript(поэтому событие не будет регистрироваться, пока всплывающее окно не будет закрыто). Если вы обернете вход.щелкните в setTimeout(function() { input.click(); }, 0) тогда он определенно будет выполнен после регистрации события, и эта теория может быть правильной.

Я не могу воспроизведите свою проблему, так что это просто чистая теория.


это происходит потому, что ваш input элемент больше не существует, когда вы закрываете диалоговое окно "Открыть файл", поэтому нет цели, на которой можно поднять onchange событие. Может быть, потому, что сборщик мусора JavaScript уже собрал этот элемент или по какой-то другой причине.

чтобы исправить это, просто сохранить input элемент где-то в DOM:

input.style.visibility='hidden';
document.body.appendChild(input);

также не забудьте сохранить ссылку на этот элемент и удалить его из DOM, когда загрузка файла завершится (я использую здесь this.set() и this.get() функции от Ext.в JS):

// after element initialization:
this.set("inputFileElement", input);
...
// in the "OnFileComplete" event handler or in some similar place:
var inputFileElement = this.get("input");
if(inputFileElement !== null && inputFileElement !== undefined)
{
    inputFileElement.parentNode.removeChild(inputFileElement);
}