Программно сгенерированный /активированный ввод файла не всегда запускает событие `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/

14 голосов | спросил Brad 30 PM00000090000001031 2018, 21:26:10

2 ответа


0
  

Цитата из вашего вопроса: Иногда (примерно 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">
ответил Bharata 18 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 18 Sep 2018 23:41:52 +0300 2018, 23:41:52
0

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

Есть ли причина, по которой вы подходите к вводу файла так, как делаете? Это потому, что вы хотите попробовать и стилизовать это?

Я прочитал эту статью и я применил то, что делал. Я обнаружил, что это работает нормально. В этой статье мы попытаемся подключить метку к входу с атрибутом 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>

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

ответил Andrew Gremlich 24 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 24 Sep 2018 20:32:22 +0300 2018, 20:32:22

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132