当記事では、ファイル選択をした時に画像をプレビューする機能をJavascriptで実装する方法について記します。
FileReaderを使用したサンプルをよく見ますが、当記事ではFileReaderを使わずに実装してみます。
また実際に触って動かせるデモも用意しております。
目次
①選択されたファイルを受け取る
下記のようなファイル選択ダイアログを通して選択されたファイルを受け取る方法について見てましょう。
<input type="file">
下記のようにonchange属性を追加することでファイル選択時に関数を呼び出すことができます。
引数にthisを指定することで関数にファイル選択ダイアログ自身のHTMLElementを渡すことができます。
<input type="file" onchange="preview(this)"> <!-- 修正 -->
onchangeで呼び出されるpreviewメソッドを定義します。
渡されたHTMLElementのfilesプロパティを参照することでFileListオブジェクトが取得できます。
今回は画像1枚をプレビューする想定なのでFileListから0番目の要素を取得します。
function preview(elem) {
const file = elem.files[0]
console.log(file)
}
②受け取ったファイルを元にimgタグを生成する
URLクラスのcreateObjectURLメソッドにFileオブジェクトを渡すことでBlobURLを生成する事ができます。
BlobURLはimgタグのsrc属性に指定することでブラウザに画像を表示する事ができます。
function preview(elem) {
const file = elem.files[0]
const image = `<img src=${URL.createObjectURL(file)}>` //追記
}
③生成したimgタグを挿入する
HTMLの方には、ファイル選択ダイアログの隣にプレビューエリアとして空のdivタグを置きます。
<input type="file" onchange="preview(this)">
<div></div> <!-- 追記 -->
Javascriptの方ではファイル選択ダイアログの隣に置いたdivタグにimgタグを挿入します。
nextElementSiblingで隣接要素が取得できるのでinnerHTMLで上書きする形になります。
function preview(elem) {
const file = elem.files[0]
const image = `<img src=${URL.createObjectURL(file)}>`
elem.nextElementSibling.innerHTML = image //追記
}
④プレビューエリアの横幅を統一する
現状ではプレビューエリアの横幅が選択される画像によってばらばらになってしまいます。
下記のようにクラスとスタイルを追加することでプレビューエリアの横幅を統一しましょう。
枠のサイズを定義し画像を横幅いっぱい広げる事で実現できます。
<!--▼ 追記 ▼-->
<style>
.preview-area { width: 300px }
.preview-area img { width: 100% }
</style>
<!--▲ 追記 ▲-->
<input type="file" onchange="preview(this)">
<div class="preview-area"></div> <!-- 修正 -->
いったんここまでで基本的な機能は整いましたが、問題点が二つあるので見ておきましょう。
⑤ファイル選択がキャンセルされたときの対応
ファイル選択ダイアログには『キャンセル』という選択肢が用意されています。
ChromeやEdgeなどのブラウザではファイル選択がキャンセルされた時、元々選択されているファイルがクリアされます(2023/9/27時点)。
そのため、ファイルはクリアされたのにプレビュー画像は残っているという状況が起こり得ます。
この状況を回避するためにファイルが空でない場合のみimgタグを挿入するようにしてみましょう。
三項演算子を用いてfile変数の条件判定結果が、trueであればimgタグを、falseであれば空文字を、image変数に入れるようにします。
function preview(elem) {
const file = elem.files[0]
const image = (file) ? `<img src=${URL.createObjectURL(file)}>` : '' //修正
elem.nextElementSibling.innerHTML = image
}
⑥画像以外のファイルが選択されたときの対応
当然ですがファイル選択ダイアログでは画像以外のファイルを選択することができてしまいます。
現状のプレビュー機能では画像以外のファイルが選択された時、本来画像が表示されるべき場所に白背景色&黒枠線付きの四角形が表示されてしまいます。
これを回避するためにファイルが画像の時のみimgタグを挿入するようにしてみましょう。
Fileオブジェクトのtypeプロパティを参照して、image/から始まっている場合のみimgタグを、それ以外は空文字を、image変数に入れるようにします。
function preview(elem) {
const file = elem.files[0]
const isOK = file?.type?.startsWith('image/') // 追記
const image = (file && isOK) ? `<img src=${URL.createObjectURL(file)}>` : '' // 修正
elem.nextElementSibling.innerHTML = image
}
ここではfileがundefinedになることがあるので、オプショナルチェーン演算子を使用しています。
デモ
実際に動かせるデモを下記に用意しました。
ファイル選択をするとプレビューが表示されると思います。
ファイル選択ダイアログを複数置いても問題なく動きます。
ソースコード全文
最後にソースコードを全文載せておきます。
<style>
.preview-area{ width: 300px }
.preview-area img { width: 100% }
</style>
<input type="file" onchange="preview(this)">
<div class="preview-area"></div>
<input type="file" onchange="preview(this)">
<div class="preview-area"></div>
<script>
function preview(elem) {
const file = elem.files[0]
const isOK = file?.type?.startsWith('image/')
const image = (file && isOK) ? `<img src=${URL.createObjectURL(file)}>` : ''
elem.nextElementSibling.innerHTML = image
}
</script>
以上、ファイル選択をした時に画像をプレビューする機能をJavascriptで実装する方法でした。