我如何检测到 <input type="file" /> Select 文件对话框上的取消按钮已被单击?

How can I detect that the Cancel Button has been clicked on a <input type="file" /> Select File Dialog?

要处理网站上的文件上传,我们必须使用隐藏的 <input type="file" /> 元素。

要找出在 Select 文件对话框中选择了什么文件,我们可以使用 onchange 事件。

但是我们如何检测用户是否点击了取消按钮? 不幸的是,没有 oncancel 事件。

有很多变通方法可以检测用户是否点击了取消按钮,但由于我描述的问题 here,其中 none 对我有效。

我投入了无数时间寻找解决方案。现在我想和你分享我的解决方案。

我使用三个事件处理程序:

  1. onchange 文件输入事件:检测文件何时被选中。

  2. onfocus window 事件:检测 Select 文件对话框何时关闭。

  3. onmousemove document.body 事件:检测交互何时不再被阻止。只有调用了这个事件,才能确定input元素的onchange事件已经被调用了

前两点很明显,您可以在大多数建议的解决方案中找到它们。但关键点是第 3 点。在其他解决方案中,我有时会遇到这样的问题:我选择了一个文件,但在 window 获得焦点之前,这个选定的文件尚未传播到 onchange 事件处理程序。

长话短说,这是我的实现:

TypeScript 解决方案:

public static selectFile(accept: string = null): Promise<File> {
    return new Promise<File>(async resolve => {
        const fileInputElement = document.createElement('input') as HTMLInputElement;
        fileInputElement.type = 'file';
        fileInputElement.style.opacity = '0';
        if (accept) fileInputElement.accept = accept;
        fileInputElement.addEventListener('change', () => {
            const file = fileInputElement.files[0];
            console.log('File "' + file.name + '" selected.');
            document.body.removeChild(fileInputElement);
            resolve(file);
        });
        document.body.appendChild(fileInputElement);
        setTimeout(_ => {
            fileInputElement.click();
            const onFocus = () => {
                window.removeEventListener('focus', onFocus);
                document.body.addEventListener('mousemove', onMouseMove);
            };
            const onMouseMove = () => {
                document.body.removeEventListener('mousemove', onMouseMove);
                if (!fileInputElement.files.length) {
                    document.body.removeChild(fileInputElement);
                    console.log('No file selected.');
                    resolve(null);
                }
            }
            window.addEventListener('focus', onFocus);
        }, 0);
    });
}

JavaScript解法:

function selectFile(accept = null) {
    return new Promise(async resolve => {
        const fileInputElement = document.createElement('input');
        fileInputElement.type = 'file';
        fileInputElement.style.opacity = '0';
        if (accept) fileInputElement.accept = accept;
        fileInputElement.addEventListener('change', () => {
            const file = fileInputElement.files[0];
            console.log('File "' + file.name + '" selected.');
            document.body.removeChild(fileInputElement);
            resolve(file);
        });
        document.body.appendChild(fileInputElement);
        setTimeout(_ => {
            fileInputElement.click();
            const onFocus = () => {
                window.removeEventListener('focus', onFocus);
                document.body.addEventListener('mousemove', onMouseMove);
            };
            const onMouseMove = () => {
                document.body.removeEventListener('mousemove', onMouseMove);
                if (!fileInputElement.files.length) {
                    document.body.removeChild(fileInputElement);
                    console.log('No file selected.');
                    resolve(null);
                }
            }
            window.addEventListener('focus', onFocus);
        }, 0);
    });
}