我正在使用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
上使用INPUT
属性时使用更多文件。)< / p>
我知道当你重新选择同一个文件时,onChange
不会被激活,但显然情况并非如此。它仅在我第一次使用此功能时才触发事件,有时仅触发。如果从对话框中选择了某些内容,则每次下一次点击通常会触发onChange
。
已经尝试在这里和周围搜索此问题,但似乎所有 onChange问题和解决方案都与重新选择相同文件的着名问题有关。
我发现这种情况发生在最新的Opera和Firefox上,从未在其他浏览器上测试过。也。我试图等待整个页面加载但结果仍然相同 - 有时它在第一次调用时不会触发onChange
。
任何人都可以向我解释为什么会这样吗? 我已经有解决方法代码,这不是问题,只需要解释为什么在创建和调用INPUT时会发生这种情况。
级联延迟
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
事件。
答案 0 :(得分:4)
这是一场竞争条件。它取决于堆栈中的内容以及在调用同步文件浏览器阻止堆栈的其余部分完成之前某些事情可能需要多长时间。使用addeventlistener,它会将一个回调排队等待以后使用,当堆栈清除时,它将被事件循环拾取。如果堆栈没有及时清除,则不会及时调用。我不能保证什么时候会运行。如果你像Pawel建议的那样使用setTimeout(fn,0),你将在放置事件监听器之后对click()函数进行排队。
这是一个精彩的视频,可视化我所谈论的所有内容:https://www.youtube.com/watch?v=8aGhZQkoFbQ
<强>更新强> 在进一步研究之后我发现了一些非常有趣的东西......它只允许一次创建多达5个元素。我这样做了:
for(var i = 0; i < 20; i += 1) {
createAndCallFileSelect()
}
有几个不同的数字...每次,任何大于5的数字只产生5个输入元素,有5个回调,而5和以下产生正确的数量。
我也尝试过递归而不是使用for循环...相同的结果。
此外,我选择的文件越大,所需的时间越长,但最终它会在处理文件后调用回调。到目前为止,这项测试都是以铬为主。
答案 1 :(得分:1)
你可以做这样的事情来触发点击动态创建的文件输入的更改
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);
我在chrome,firefox,opera和IE中测试了这个。它的工作原理
答案 2 :(得分:0)
事件监听器input.addEventListener ("change" ...
未立即注册。它就像在setTimeout(fn, 0)
中包装代码一样,这使得它被添加到执行队列的末尾。
但是input.click();立即打开一个文件选择弹出窗口,暂停JavaScript(因此事件赢得了注册,直到弹出窗口关闭)。如果你在setTimeout(function() { input.click(); }, 0)
中包装input.click,那么它肯定会在事件被注册后执行,这个理论可能是正确的。
我无法重现你的问题所以这只是纯粹的理论。
答案 3 :(得分:0)
之所以会这样,是因为当您关闭“打开文件”对话框窗口时,您的input
元素不再存在,因此没有目标引发onchange
事件。可能是因为JavaScript的垃圾收集器已经收集了此元素,或者是由于其他原因。
要解决此问题,只需将input
元素保存在DOM中:
input.style.visibility='hidden';
document.body.appendChild(input);
也不要忘记保存指向该元素的链接,并在文件上传完成后将其从DOM中删除(我在这里使用Ext.js的this.set()
和this.get()
函数):
// 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);
}
答案 4 :(得分:0)
我知道这个问题与解决方法无关,但是我来这里寻求解决方案。这在Chrome上对我有用。我只是将sys:1: DtypeWarning: Columns (11,13,39,40,41) have mixed types. Specify dtype option on import or set low_memory=False.
sys:1: DtypeWarning: Columns (13) have mixed types. Specify dtype option on import or set low_memory=False.
Index(['SalesID', 'SalePrice', 'MachineID', 'ModelID', 'datasource',
'auctioneerID', 'YearMade', 'MachineHoursCurrentMeter', 'UsageBand',
'saledate',
...
'Unnamed: 43', 'Unnamed: 44', 'Unnamed: 45', 'Unnamed: 46',
'Unnamed: 47', 'Unnamed: 48', 'Unnamed: 49', 'Unnamed: 50',
'Unnamed: 51', 'Unnamed: 52'],
dtype='object', length=106)
移到了函数之外。之所以有效,是因为(如bside所建议)它可以防止let input
被垃圾回收。我只希望一次打开一个打开文件对话框,因此这里可以使用单例模式。
input