取消input type="file"的事件

67

我正在使用标准的文件上传输入框,并寻找一种方法来将函数附加到事件上,当用户点击/按下“取消”按钮(或从“选择文件”对话框中退出)时触发。

我找不到在所有浏览器和平台上都能一致运行的事件。

我已经阅读了这个问题的答案:捕获input type=file上的cancel事件, 但它们并不起作用,因为大多数浏览器在取消选择文件对话框后不会触发change事件。

我正在寻找一个纯JS解决方案,但也接受jQuery解决方案。

有没有人成功解决这个问题?


由于不是所有浏览器都执行.change(),所以应该寻找被调用的事件。有没有特定的浏览器您希望与之配合使用? - Twisty
在 https://jsfiddle.net/Twisty/j18td9cs/ 上进行了一些测试,在 FF 中,由于“取消”是浏览器对话框的元素,我只能检查是否选择了文件。我注意到如果我选择一个文件,然后再浏览第二次,然后点击取消,它会保留文件值...所以这里没有帮助。可以在页面上创建自己的取消按钮。尝试查看是否更新了某些内容或返回是否选择了取消,例如使用 confirm()prompt() - Twisty
这个回答解决了你的问题吗?如何检测文件输入框上的取消按钮被点击? - leonheess
17个回答

0
您可以按照以下步骤取消活动:
input.addEventListener('cancel', e => { ... })

文档


或: input.oncancel = (e) => { 不附加文件() } - undefined

0

0

如果你可以使用文件系统访问APIshowOpenFilePicker方法,那么有另一种选择。

在撰写本回答时,该API已经完全规范化,但只有Chromium浏览器(Chrome和Edge)支持,但希望它很快会更普遍可用。

(请注意,以下代码片段在沙箱或跨域iframe中不起作用,因此无法在StackOverflow、CodePen和jsfiddler上运行,您必须拥有HTTPS环境来进行测试)

document.querySelector("button").addEventListener("click", async () => {
    try {
        const file = await globalThis.showOpenFilePicker({
            types: [
                {
                    description: "Images",
                    accept: {
                        "image/*": [".png", ".gif", ".jpeg", ".jpg"],
                    },
                }
            ]
        });

        console.log("You picked: ", file[0].name);
    } catch (e) {
        if (e.code === e.ABORT_ERR) {
            alert("You cancelled.")
        } else {
            throw e;
        }                
    }
});
<button>Pick a file</button>


0
我遇到了一个问题,当我在input type="file"元素上点击取消按钮时,希望函数什么也不做。如果选择了某些东西并单击打开按钮,则希望我的函数执行某些操作。示例仅显示了该方法,我剥离了它打开后的操作。我添加了警报,只是为了让您看到在单击取消时没有从对话框返回文件名。 这是我使用的一种方法,它很简单但有效。

 function openFileOnClick(){
    document.getElementById("fileSelector").value = "";
    document.getElementById("fileSelector").files.length = 0;            
    document.getElementById("fileSelector").click();
    if(document.getElementById("fileSelector").files.length >= 1){
        alert(document.getElementById("fileSelector").value);
        //Do something 
    }
    else{
        alert(document.getElementById("fileSelector").value);
        //Cancel button has been called.
    }
}
<html>
<head>
</head>
<body>
<input type="file" id="fileSelector" name="fileSelector" value="" style="display:none;"  />
<input type="button" value="Open File" name="openFile" onclick="openFileOnClick();" />
</body>
</html>


你能否详细解释一下你的代码片段是在做什么或为什么它有效? - danjuggler
我遇到了一个问题,当我在input type="file"元素上点击取消按钮时,希望函数什么也不做。如果选择了某些东西并点击打开按钮,则希望我的函数执行某些操作。上面的示例仅显示了方法,我剥离了它打开后要执行的内容。我加入了警报,只是为了让您看到对话框中没有返回文件名。 - DJones
1
对于这种情况,提供一个 fiddle 是有帮助的,这样我们就可以观察行为。 - GWR
1
@DJones,如果您能编辑您的答案并添加该信息,那将非常好! - danjuggler
2
我将它转换为片段进行测试。警告框在对话框完成之前弹出。也许您在为演示而剥离代码时删除了一些重要内容? - Regular Jo
1
不,这段代码只能在IE中运行。在普通浏览器中,点击会触发异步代码。只有在IE中,点击函数才是同步的。 - Cosmin Popescu

0
我从this的答案中得到启发,成功在Chrome和Safari中实现了这个功能,但是在Firefox中似乎无法以同样的方式工作——当对话框关闭时,focus事件从未被触发。看起来Firefox使用了cancel事件(更多信息请参见here)。我已经在最新版本的Chrome、Safari和Firefox中成功测试了这个功能:
function selectFiles(
  options?: {
    /** Allow the selection of multiple files */
    multiple?: boolean,
    /** Restrict the selection to certain types of files (ex: `.txt`) */
    accept?: Array<string>
  }
): Promise<{
  /** The list of selected files (empty if none selected) */
  files: Array<File>,
  /** The event that prompted the dialog to close */
  event: Event
}> {
  return new Promise((resolve) => {
    const fileSelector: HTMLInputElement = document.createElement('input');

    fileSelector.type = 'file';
    fileSelector.style.display = 'none';
    fileSelector.multiple = !!options?.multiple;

    if (Array.isArray(options?.accept)) {
      fileSelector.accept = options.accept.join(',');
    }

    let currTimeout;
    const resolveWithFiles = (event?: Event) => {
      clearTimeout(currTimeout);
      currTimeout = setTimeout(() => {
        // cleanup
        window.removeEventListener('focus', resolveWithFiles);
        fileSelector.remove();
        // resolve with file array and the event associated with
        // what prompted the dialog to close
        resolve({ files: Array.from(fileSelector.files || []), event });
      }, 300);
    };

    // EVENTS
    // "cancel" event in Chrome and Safari
    window.addEventListener('focus', resolveWithFiles);
    // "cancel" event in Firefox
    fileSelector.addEventListener('cancel', resolveWithFiles);
    // files selected
    fileSelector.addEventListener('change', resolveWithFiles);

    // INITIATE
    // open the selection window
    document.body.append(fileSelector);
    fileSelector.click();
  });
}

0

ES2022 方式

创建文件选择器服务

// wait function to delay 
const wait = (ms) => new Promise((res) => setTimeout(res, ms));

class FilePickerServiceK {
    getFileInput() {
        if (this.ref)
            return this.ref;
        const input = document.createElement('input');
        input.type = 'file';
        this.ref = input;
        return this.ref;
    }
    async pick(opt = {}) {
        const input = this.getFileInput();
        input.multiple = opt.multiple;
        const onWindowFocusP = new Promise((res) => window.addEventListener('focus', res, {once: true}));
        input.click();
        await onWindowFocusP;
        await wait(100);
        const files = Array.from(input.files ?? []);
        input.value = '';
        return files;
    }
}
const FilePickerService = new FilePickerServiceK();
// for demo
const button = document.createElement('button');
button.innerHTML = 'Pick File';
document.body.appendChild(button);
const div = document.createElement('div');
document.body.appendChild(div);
const handle = async () => {
    const [file] = await FilePickerService.pick();
    div.innerHTML = file ? file.name : 'cancelled';
};
button.addEventListener('click', handle);


0
注意,change事件将在上传新文件或取消输入事件时触发,因此最好在change事件中检测取消操作。
$("input[type='file']").on('change', changeCB);
function changeCB(ev){
  if (this.files && this.files.length > 0) {
    // upload new file
    alert('upload');
  } else {
    // cancel upload no files received when event change casted
    alert('cancel');
  }
}

如您在更改事件中所见,如果没有文件,那意味着用户取消了上传,因此输入值将被清除并为空,所以在这种情况下,this.files.length 将等于 0。

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