如何检测<input type="file"/>选择文件对话框中的取消按钮是否被点击?

3
为了在网站上处理文件上传,我们必须使用一个隐藏的<input type="file" />元素。
为了找出在“选择文件”对话框中选择了哪个文件,我们可以使用onchange事件。
但是,如何检测用户是否点击了取消按钮呢? 不幸的是,没有oncancel事件。
有很多解决方案可以检测用户是否点击了取消按钮,但由于我在这里描述的问题(链接),它们中的任何一个都不能可靠地工作。
1个回答

8

我已经花费无数个小时寻找解决方案。现在我想与您分享我的解决方案。

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

  1. 文件输入的onchange事件:用于检测是否选择了文件。

  2. window上的onfocus事件:用于检测选择文件对话框何时关闭。

  3. document.body上的onmousemove事件:用于检测交互是否不再被阻塞。只有当调用此事件时,您才可以确定输入元素的onchange事件已被调用。

前两个点很明显,您会在大多数提议的解决方案中找到它们。但是关键点是第三点。在其他解决方案中,我有时会遇到这样的问题:我选择了一个文件,但在窗口获取焦点之前,这个选定的文件还没有传播到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);
    });
}

它是有效的,但我发现一个特殊情况(在Chrome上发现):当在对话框中双击文件以选择它时,输入change事件似乎没有足够的时间传播,控制台会打印出“未选择文件”。 - AlexandreS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接