《 1:15 PM 公開/更新》
ウェブ上の入力フォームを使って画像などのファイルをアップロードさせたい際には、input要素で作るファイル選択ボックスを使います。しかし、このファイル選択フォームはCSSを使って装飾しにくいので、アイコン化(ボタン化)して掲載すると便利です。しかし、それだと「今いくつファイルを選択しているのか?」が分かりにくくなるデメリットがあるため、JavaScriptを併用して「選択中の画像プレビュー」を並べて表示できるようにすると大変便利になります。画像をアップロードさせることなく、ウェブ上にプレビュー表示する方法を解説。
ウェブ上の入力フォームを使って画像などのファイルをアップロードさせたい際には、input要素で作るファイル選択ボックスを使います。しかし、このファイル選択フォームはCSSを使って装飾しにくいので、アイコン化(ボタン化)して掲載すると便利です。しかし、それだと「今いくつファイルを選択しているのか?」が分かりにくくなるデメリットがあるため、JavaScriptを併用して「選択中の画像プレビュー」を並べて表示できるようにすると大変便利になります。
input要素で作るファイル選択ボックスをアイコン化(ボタン化)する方法だけについては、過去に当サイトのCSS Tipsコーナー内で解説しました。記事「ファイル送信フォームのUIをアイコン(ボタン)表示に変える方法」をご参照下さい。HTMLでlabel要素を活用し、CSSで装飾するだけで実現できます。とても簡単です。
その結果、ファイル選択フォームを以下のようなボタン形状に装飾できますが、それだけだと「いくつのファイルが選択されているのか?」も表示できませんし、選択中ファイルのプレビュー表示もできない不便さがあります。
そこで今回は、さらにJavaScriptを加えて、選択中の画像を(アップロードすることなく)プレビュー表示する方法を解説します。アップロード前にプレビュー表示できるので、動作は軽快ですし、無駄な通信も発生しないため便利です。
まずは、HTMLソースを記述しましょう。
下記のHTMLソースには、以下のような要素が含まれています。
HTMLソース
<form action="https://example.com/post/form/" method="post" enctype="multipart/form-data"> <p> <label> <span class="filelabel" title="ファイルを選択"> <img src="camera-orange-rev.png" width="32" height="26" alt="+画像"> <span id="selectednum">選択</span> </span> <input type="file" name="datafile" id="filesend" multiple accept=".jpg,.gif,.png,image/gif,image/jpeg,image/png"> </label> <span id="previewbox"></span> <input type="reset" value="Reset" onclick="resetPreview();"><!-- ※これはリセットボタン(省略可) --> </p> </form>
ファイル選択フォームをボタン化するための部分については、CSS Tips側の記事「ファイル送信フォームのUIをアイコン(ボタン)表示に変える方法」で解説済みなのでここでは省略します。
※上記のソースでは、11行目にリセットボタンを配置しています。これは省略しても動作に問題はありません。後述するJavaScriptで作るリセット用関数resetPreviewを単独で実行しています。
HTMLソースの段階では、単にプレビューを表示するための空間を用意しているだけです。
今の段階では、下記のように普通のファイル選択フォームとして表示されるだけです。
ここから、CSSで装飾を加えて、JavaScriptで動作を加えます。
次に、先ほど書いた「ファイル選択フォームとプレビュー用領域を表示するHTML」に対して、CSSで装飾を追加しましょう。
例えば、下記のようにCSSソースを記述します。
CSSソース
.filelabel { background-color: orange; /* 背景色 */ color: white; /* 文字色 */ border: 2px solid orange; /* 枠線 */ border-radius: 3em; /* 角丸 */ padding: 12px 9px; /* 内側の余白 */ display: inline-block; /* インラインブロック化 */ } .filelabel img { vertical-align: bottom; /* 画像の垂直方向の配置 */ } .filelabel:hover { opacity: 0.5; /* 半透明 */ border: 2px solid red; /* 枠線(赤色) */ cursor: pointer; /* マウス形状(ポインタ) */ } #filesend { display: none; /* 本来のファイル選択フォームは非表示に */ } #previewbox { display: inline-block; /* プレビュー領域をボタンと横並びに配置する */ vertical-align: middle; /* プレビュー画像の垂直方向の配置 */ }
上記のCSSソースのうち、1行目~19行目までは、ファイル選択フォームをボタン化するための部分です。それらはCSS Tips側の記事「ファイル送信フォームのUIをアイコン(ボタン)表示に変える方法」で解説済みなのでここでは省略します。
ここで加えているのは、プレビュー領域を装飾する4行だけです。
特別なことは何もしていません。単に、
というだけです。
この段階では、下記のようにファイル選択フォームが独自のボタン形状で見えるようになります。
クリックすればもちろんファイル選択機能が働きますが、本来のファイル選択フォームを非表示にしているため、「何のファイルを選択中なのか」(または何も選択されていない状態なのか)が見えません。そこで、次にJavaScriptで画像プレビュー機能を加えます。
次に、今回の本題である、選択された画像を送信前にプレビューするためのJavaScriptソースを書きましょう。
まずは、記述する必要のあるJavaScriptソースの全体を掲載しておきます。
JavaScriptソース
// ▼①ファイル選択フォームの更新イベントに処理を追加 document.getElementById("filesend").addEventListener('change', function(e) { var files = e.target.files; previewUserFiles(files); }); // ▼②選択画像をプレビュー function previewUserFiles(files) { // 一旦リセットする resetPreview(); // 選択中のファイル1つ1つを対象に処理する for (var i = 0; i < files.length; i++) { // i番目のファイル情報を得る var file = files[i]; // 選択中のファイルが画像かどうかを判断 if( file.type.indexOf("image") < 0 ) { /* 画像以外なら無視 */ continue; } // ファイル選択ボタンのラベルに選択個数を表示 document.getElementById("selectednum").innerHTML = (i+1) + "個選択中"; // 画像プレビュー用のimg要素を動的に生成する var img = document.createElement("img"); img.classList.add("previewImage"); img.file = file; img.height = 100; // プレビュー画像の高さ // 生成したimg要素を、プレビュー領域の要素に追加する document.getElementById('previewbox').appendChild(img); // 画像をFileReaderで非同期に読み込み、先のimg要素に紐付けする var reader = new FileReader(); reader.onload = (function(tImg) { return function(e) { tImg.src = e.target.result; }; })(img); reader.readAsDataURL(file); } } // ▼③プレビュー領域をリセット function resetPreview() { // プレビュー領域に含まれる要素のすべての子要素を削除する var element = document.getElementById("previewbox"); while (element.firstChild) { element.removeChild(element.firstChild); } // ファイル選択ボタンのラベルをデフォルト状態に戻す document.getElementById("selectednum").innerHTML = "選択"; }
※上記のソースは、MDNサイト内の「Web アプリケーションからファイルを扱う」ページに含まれる「例: ユーザが選択した画像のサムネイルを表示」に掲載されているソースをそのままコピーして使っている箇所が多くあります。
説明のしやすさから、①→③→②の順で以下に説明します。
まずは、1行目~5行目です。
JavaScriptソース
// ▼①ファイル選択フォームの更新イベントに処理を追加 document.getElementById("filesend").addEventListener('change', function(e) { var files = e.target.files; previewUserFiles(files); });
ファイル選択フォームHTMLソースでは、id属性を使って「filesend」というid名を割り振りました。
なので、getElementByIdメソッドを使って document.getElementById("filesend")
のように書けば、ファイル選択フォームに対して何らかの操作ができます。
ここではファイルの選択状態が変化する度にプレビュー処理を実行させたいわけですから、onchangeイベントを利用すれば良いでしょう。
addEventListenerメソッドを使って 対象.addEventListener('change', function(e) { ~処理~ });
などと書けば、対象のonchangeイベント発生時に独自の処理を実行できます。
これが2行目に書いてあることです。
2行目でaddEventListenerメソッドの第2引数に無名関数を記述していますが、 function(e) { ~ }
と書くことで、eにはイベントオブジェクトが渡されます。
それによって、3行目にあるように var files = e.target.files;
で、選択されたファイル情報の一覧を変数filesに格納できます。
最後に、得られた方法(=files変数の中身)を、メイン処理を実行する関数previewUserFilesに渡しています。
関数previewUserFilesは、7行目~33行目に書いています。解説は後述します。
次に、34行目以降を先に解説しておきます。ここでは、プレビュー表示を取り消して、ファイル選択ボタンのラベルを初期化しています。
JavaScriptソース
// ▼③プレビュー領域をリセット function resetPreview() { // プレビュー領域に含まれる要素のすべての子要素を削除する var element = document.getElementById("previewbox"); while (element.firstChild) { element.removeChild(element.firstChild); } // ファイル選択ボタンのラベルをデフォルト状態に戻す document.getElementById("selectednum").innerHTML = "選択"; }
プレビュー用として動的に生成するimg要素は、ユーザがファイルの選択を解除しても勝手には消えません。なので、それらを消す処理を書く必要があります。それがここで作っているresetPreview関数です。
プレビュー領域(id属性値がpreviewboxの要素)の中に含まれる子要素をひたすら削除します。
画像プレビュー用に設けた領域(span要素)には、id属性を使って「previewbox」というid名を割り振りました。
なので、getElementByIdメソッドを使って document.getElementById("previewbox")
のように書けば、その要素に対して何らかの操作ができます。(ここでは、変数elementに格納しています。)
プレビュー領域のspan要素(=変数element)内にある先頭の子要素は element.firstChild
で得られます。
その子要素を削除するには、removeChildメソッドを使って element.removeChild(element.firstChild);
のように書けます。
while文でループすることで、存在する子要素を全て削除することができます。
ファイル選択ボタンのラベルには、選択中のファイル数を表示する仕様にしていますので、無選択状態を示す初期値として「選択」という文字列を表示させます。
フォーム選択ボタンのラベルテキストには、span要素にid属性値「selectednum」を指定していましたので、getElementByIdメソッドとinnerHTMLプロパティを使って書き換えています。
最後に、7行目~33行目です。
ここがメインの処理ですね。
関数previewUserFilesは、大きく分けて以下のような構成になっています。
これは単に、先ほど作成した関数resetPreviewを実行しているだけです。
JavaScriptソース(抜粋)
// 一旦リセットする resetPreview();
選択中のファイル情報は変数filesに格納されています。
選択されているファイルの総数は、files.length
で得られます。
したがって、for文を使って for(var i = 0; i < files.length; i++) { ~処理内容~ }
などと書くことで、ファイルの個数分だけループできます。選択中のファイルが1つもない場合は、ループ内部の処理は1度も実行されません。
JavaScriptソース(抜粋)
// i番目のファイル情報を得る var file = files[i]; // 選択中のファイルが画像かどうかを判断 if( file.type.indexOf("image") < 0 ) { /* 画像以外なら無視 */ continue; }
files[i]
でi番目のファイル情報を得て、変数fileに格納しています。
ここでは画像ファイルだけを対象にしてプレビューさせるため、ファイルタイプを調べて「image」という文字列が含まれているかどうかを確認しています。
もし含まれていない場合は、「画像ではない」と判断して次のループへ進みます。
JavaScriptソース(抜粋)
// ファイル選択ボタンのラベルに選択個数を表示 document.getElementById("selectednum").innerHTML = (i+1) + "個選択中";
ファイル選択ボタンのラベルには、id名「selectednum」が付加されています。
getElementByIdメソッドとinnerHTMLプロパティを使って、ラベルの表示に選択中のファイル個数を表示させています。
JavaScriptソース(抜粋)
// 画像プレビュー用のimg要素を動的に生成する var img = document.createElement("img"); img.classList.add("previewImage"); img.file = file; img.height = 100; // プレビュー画像の高さ // 生成したimg要素を、プレビュー領域の要素に追加する document.getElementById('previewbox').appendChild(img);
プレビュー画像を表示するためには、画像1つ1つに対して、表示用のimg要素を用意しなければなりません。
ファイルがいくつ選択されるのかが事前に分からない以上、img要素は動的に生成するしかありません。
そこで、createElementメソッドを使ってimg要素を生成し、必要な属性値を付加しています。
動的に生成されたプレビュー表示用のimg要素をCSSで装飾したい場合に備えて、23行目ではimg.classList.add("previewImage");
のようにして、class名「previewImage」を加えています。特にCSSで装飾しないなら、この記述は不要です。
また、プレビューの大きさを制限するため、25行目ではheight属性値に高さを入れています。(CSSで大きさを制限するなら、この行は不要ですが。)
最後に、プレビュー表示領域用の要素(id=”previewbox”)に対して、appendChildメソッドを使って、今作成したimg要素を追加しています。
JavaScriptソース(抜粋)
// 画像をFileReaderで非同期に読み込み、先のimg要素に紐付けする var reader = new FileReader(); reader.onload = (function(tImg) { return function(e) { tImg.src = e.target.result; }; })(img); reader.readAsDataURL(file);
FileReaderオブジェクトを使うと、閲覧者のローカル環境にあるファイルを非同期で読み込むことができます。この方法なら、ユーザにファイルをアップロードさせることなく、ブラウザ上に画像を表示できます。
ここでは、29行目で新しいFileReaderオブジェクトを生成し、
30行目で、onloadイベントに処理(後述)を記述し、
31行目で、readAsDataURLメソッドを呼んで、ローカルにあるファイルを非同期で読み込みます。
画像ファイルの読み込みが完了すると、onloadイベントが発生するので30行目に書いた処理が実行されます。
読み込まれた画像は、Base64でエンコードされたdata:URLの形で得られますので、ここではそのデータを、img要素のsrc属性値に指定しています。
これによって、ローカルにファル画像ファイルのプレビューが実現します。
上記のHTML+CSS+JavaScriptソースを実際に表示させると、以下のように見えます。
※画像以外のファイルが選択された場合には、プレビューは表示されませんし選択個数にもカウントされませんが、「ファイルの選択状態」はそのままです。もしユーザがそのままの状態で送信ボタンを押せば、画像以外のファイルもアップロードされる点には注意してください。最終的には、ファイルを受け取るサーバ側でデータをより分ける必要があります。
上記の『独自ボタン型ファイル選択フォーム』の動作サンプルは、以下のように動作するはずです。
以上、ファイル送信フォームのUIをアイコン(ボタン)表示に変えた上で、選択中ファイルを送信前にプレビュー表示する方法でした。
ぜひ、使ってみて下さい。
()
(前の記事) « 表示文章中の、指定の単語だけを動的に強調表示(ハイライト)する方法
前後のJavaScript TIPS
< 旧 / 新 >
(次の記事) ページを移動させたり、現在URLを構成部分別に得る方法 »
▼このページに関連しそうな記事が約8本くらい自動表示されています。(たぶん)