发现问题
前几天,在使用 input file 控件监听用户文件选择弹出框的取消动作时,出现了很大困难,input file 文件打开动作只有 onchange 事件才能触发,因此在 onchange 事件内对取消动作判定无效,因为此时 onchange 事件没有触发,所以函数内的代码也不会执行。html 页面代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>input file</title>
</head>
<body>
<a href="javascript:fclick();">浏览</a><p></p>
<input type="file">
<script>
document.querySelector('input').style.display = 'none';
function fclick() {
document.querySelector('p').innerHTML = "打开文件...";
document.querySelector('input').click();
}
document.querySelector('input').onchange = function () {
files = this.files[0];
if (files) {
console.log("打开文件", files.name)
} else {
console.log('取消');
}
}
</script>
</body>
</html>
提出问题
上面代码存在两个问题:
1, 如果两次选择打开相同文件,则第二次打开不会执行。
2, 若在页面对话框中选择了取消,无法监听或绑定用户取消动作。
这都是 input files 设计带来的问题,因为 input.value 值未改变, onchange 事件不会被触发。
解决问题
解决第1个问题的思路,大部分浏览器为了安全限制 input.value 值不可修改,但是可以被置空。因此,onchange 文件打开任务完成后,将缓存 input.value 值置空,第二次打开相同的文件之前 input.value 值是空的,onchange 就会被触发。
document.querySelector(‘input’).value = “”; //有效
document.querySelector(‘input’).value = “data.xls”; //无效
解决第2个问题的思路,文件打开对话框弹出后,当前页面失去焦点,当文件选择对话框关闭(无论是确定还是取消),页面将重新获取焦点。可以绑定页面获取焦点事件,判定用户是否取消了文件选择。
需要注意的是,浏览器页面获取焦点事件早于 onchange 事件约20毫秒,需要页面绑定的事件滞后执行,使用 setTimeout 即可。
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script src="jquery-3.6.0.min.js"></script>
<title>input控件研究</title>
</head>
<body>
<button>打开图片</button><br><img><input type="file">
<script>
var inpt = $('input').hide(), FLAG = 0;
$("button").bind("click", function () {
inpt.click();
FLAG = 1;
});
inpt.change(function (e) {
var files = e.target.files[0];
if (files) {
FLAG = 2; //文件打开标志
//reader 对象读取打开文件内容
var reader = new FileReader();
reader.readAsDataURL(files);
reader.onload = function () {
$("img").attr({ "src": this.result, "height": "320px" });
//若两次打开同一个文件,则change事件不会发生,故须清空
inpt.val('');
}
}
});
$(window).focus(f => {
setTimeout(e => {
if (FLAG == 1) {
FLAG = 0;
console.log('取消');
}
}, 100);
});
</script>
</body>
</html>
至此,两个问题完美解决。
目前网上最流行的 fileinput 文件上传插件,在最新的 5.2.6 版本似乎也没解决这个问题,测试发现,当文件选择框被取消时,载入文件的 loadding 动画会一直转圈,无法准确监测到用户取消动作。