javascript - 以编程方式生成/激活的文件输入并不总是触发 `input` 事件

标签 javascript html google-chrome browser blink

我的网络应用程序上有一个按钮,它在点击事件处理程序中有以下代码:

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

有时(大约八分之一),选择文件后,input 事件不会在选择文件后触发。我猜这是围绕元素生命周期的浏览器错误。

除了将元素附加到页面并稍后将其删除之外,有什么办法可以解决这个问题?如今在现代浏览器中处理这个问题的正确方法是什么?

我正在 Windows 上使用 Google Chrome 进行测试。

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.

我可以使用 Opera(版本 55.0.2994.61,目前最新版本)使用 Google Chrome browser engine "Blink" 通过 inputchange 事件确认此行为. 25 人中大约有 1 人发生这种情况。

解决方案

发生这种情况是因为有时您的输入元素对象在文件对话框关闭后被删除,因为它不再使用。当它发生时,您没有可以接收 inputchange 事件的目标。

要解决这个问题,只需在创建隐藏对象后将输入元素添加到 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://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.

我以前建议的解决方法(现在不相关)

我们可以使用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://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">

关于javascript - 以编程方式生成/激活的文件输入并不总是触发 `input` 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52103269/

相关文章:

javascript - 容器 div 覆盖其内容

html - Bootstrap 列被压扁

javascript - 未捕获的语法错误 : Unexpected token } in Google Chrome

python - OSX 10.11 上的 Headless Selenium + Xvfb + Chrome

javascript - 在 iframe 中放置通用菜单以从单个位置加载的任何通用方法

javascript - Webpack 工作流程有效地拆分 vendor 和应用程序代码

javascript - 选择 JS 按关键字搜索

javascript - 用函数定义变量

javascript - 根据其他选择列表中的选择重置(空)knockout.js 选择列表

html - 允许拖放包含 iframe 的元素