Программно сгенерированный / активированный ввод файла не всегда запускает событие `input` - PullRequest
0 голосов
/ 30 августа 2018

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

Есть ли способ добавить элемент на страницу и удалить его позже? Какой правильный способ справиться с этим в современных браузерах в наши дни?

Я тестирую с Google Chrome в Windows.

JSFiddle: http://jsfiddle.net/pja1d5om/2/

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

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

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

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

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

0 голосов
/ 18 сентября 2018

Цитата из вашего вопроса: Иногда (примерно 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">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...