处理用户可以取消文件输入的所有各种方式变得很棘手。
- 在大多数浏览器上,文件选择器会立即打开并将用户带出浏览器。我们可以使用
window.focus 事件来检测他们何时回来,而无需选择任何东西来检测取消
- 在 ios 浏览器上,用户首先会看到一个 ios 模式,让他们在相机 - 与 - 画廊之间进行选择。用户可以通过点击离开模式从这里取消。所以,我们可以使用
window.touchend 来检测这个
- 可能还有其他浏览器和案例在取消时表现不同,这也尚未发现
在实现方面,您可以使用addEventListener 来确保您不会替换窗口上可能已经存在的其他事件侦听器 - 并在事件侦听器触发后轻松清理它。例如:
window.addEventListener('focus', () => console.log('no file selected'), { once: true });
这是一个示例,说明如何使用它以编程方式获取图像,处理上面列出的注意事项(打字稿):
/**
* opens the user OS's native file picker, returning the selected images. gracefully handles cancellation
*/
export const getImageFilesFromUser = async ({ multiple = true }: { multiple?: boolean } = {}) =>
new Promise<File[]>((resolve) => {
// define the input element that we'll use to trigger the input ui
const fileInput = document.createElement('input');
fileInput.setAttribute('style', 'visibility: hidden'); // make the input invisible
let inputIsAttached = false;
const addInputToDom = () => {
document.body.appendChild(fileInput); // required for IOS to actually fire the onchange event; https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
inputIsAttached = true;
};
const removeInputFromDom = () => {
if (inputIsAttached) document.body.removeChild(fileInput);
inputIsAttached = false;
};
// define what type of files we want the user to pick
fileInput.type = 'file';
fileInput.multiple = multiple;
fileInput.accept = 'image/*';
// add our event listeners to handle selection and canceling
const onCancelListener = async () => {
await sleep(50); // wait a beat, so that if onchange is firing simultaneously, it takes precedent
resolve([]);
removeInputFromDom();
};
fileInput.onchange = (event: any) => {
window.removeEventListener('focus', onCancelListener); // remove the event listener since we dont need it anymore, to cleanup resources
window.removeEventListener('touchend', onCancelListener); // remove the event listener since we dont need it anymore, to cleanup resources
resolve([...(event.target!.files as FileList)]); // and resolve the files that the user picked
removeInputFromDom();
};
window.addEventListener('focus', onCancelListener, { once: true }); // detect when the window is refocused without file being selected first, which is a sign that user canceled (e.g., user left window into the file system's file picker)
window.addEventListener('touchend', onCancelListener, { once: true }); // detect when the window is touched without a file being selected, which is a sign that user canceled (e.g., user did not leave window - but instead canceled the modal that lets you choose where to get photo from on ios)
// and trigger the file selection ui
addInputToDom();
fileInput.click();
});