どこからでもファイルダイアログを呼び出したい

ファイルダイアログとは
inputタグの type にfileを指定し、クリックすると出てくるあれです。現状、ファイルダイアログ自体を直接プログラムから呼び出す方法は無いようで、 ファイルダイアログを呼び出す方法として、以下のような方法が解説されています。
- input タグを hidden にして、label をボタンのように見せる CSS ハック
- input タグを hidden にして、getElement で DOM を取得し、
clickを呼び出す
これらの方法は、同じファイルを複数回選択したときに、input のchangeイベントが発生しなかったり余計な要素のレンダリングが必要であったりします。 ファイルダイアログを呼び出して、ファイルを受け取りたいだけなのに、スマートではありません。
今回は、ファイルダイアログを簡単に呼び出し、ファイルを受け取る方法を解説します。
実装
export const fileDialog = (
accept: string = '*',
multiple: boolean = false
): Promise<FileList> =>
new Promise((resolve, reject) => {
const input = document.createElement('input')
input.type = 'file'
input.multiple = multiple
input.accept = accept
input.onchange = () => {
const { files } = input
if (files) {
resolve(files)
} else {
reject(Error('Not receive the FileList'))
}
input.remove()
}
input.click()
})ポイントとしては、ファイルダイアログを呼び出すだけなら、input 要素のレンダリングは必要ありません。 そのかわりに、createElementで input 要素を作成しclickで type=file の input タグをクリックしたときと、同じイベントを発生できます。
input 要素ではonchangeの発火後、inputのfilesプロパティで、ファイルダイアログで選択されたファイルへアクセスできます。型としてはFileListまたはnullを受け取るので、null チェックのために分岐します。
Promise でラップすることにより、onchangeを同期的に処理できるため、呼び出し側で awaitを付ければ同期的に処理できます。サンプルでは、filesがnullの場合は、rejectとして処理しています。
また、引数としてacceptとmultipleを受け取るようにしています。どちらも input タグに存在する属性で、acceptには受け取るファイルの種類を指定します。multipleをtrueにすると複数のファイルを選択できます。
この関数をボタンなどのクリックイベント等によって呼び出すことで、ファイルダイアログを開き、ファイルを受け取ることができます。
ロジックとビューを分離でき、シンプルに実装できるため、ぜひ参考にしてください。