《 1:15 PM 公開/更新》
ウェブ上の入力フォームを使って画像などのファイルをアップロードさせたい際には、input要素で作るファイル選択ボックスを使います。しかし、このファイル選択フォームはCSSを使って装飾しにくいので、アイコン化(ボタン化)して掲載すると便利です。しかし、それだと「今いくつファイルを選択しているのか?」が分かりにくくなるデメリットがあるため、JavaScriptを併用して「選択中の画像プレビュー」を並べて表示できるようにすると大変便利になります。画像をアップロードさせることなく、ウェブ上にプレビュー表示する方法を解説。
ウェブ上の入力フォームを使って画像などのファイルをアップロードさせたい際には、input要素で作るファイル選択ボックスを使います。しかし、このファイル選択フォームはCSSを使って装飾しにくいので、アイコン化(ボタン化)して掲載すると便利です。しかし、それだと「今いくつファイルを選択しているのか?」が分かりにくくなるデメリットがあるため、JavaScriptを併用して「選択中の画像プレビュー」を並べて表示できるようにすると大変便利になります。
input要素で作るファイル選択ボックスをアイコン化(ボタン化)する方法だけについては、過去に当サイトのCSS Tipsコーナー内で解説しました。記事「ファイル送信フォームのUIをアイコン(ボタン)表示に変える方法」をご参照下さい。HTMLでlabel要素を活用し、CSSで装飾するだけで実現できます。とても簡単です。
その結果、ファイル選択フォームを以下のようなボタン形状に装飾できますが、それだけだと「いくつのファイルが選択されているのか?」も表示できませんし、選択中ファイルのプレビュー表示もできない不便さがあります。
そこで今回は、さらにJavaScriptを加えて、選択中の画像を(アップロードすることなく)プレビュー表示する方法を解説します。アップロード前にプレビュー表示できるので、動作は軽快ですし、無駄な通信も発生しないため便利です。
まずは、HTMLソースを記述しましょう。
下記のHTMLソースには、以下のような要素が含まれています。
HTMLソース
1 2 3 4 5 6 7 8 9 10 11 12 13 | < 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ソース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | .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ソース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // ▼①ファイル選択フォームの更新イベントに処理を追加 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ソース
1 2 3 4 5 | // ▼①ファイル選択フォームの更新イベントに処理を追加 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ソース
34 35 36 37 38 39 40 41 42 43 | // ▼③プレビュー領域をリセット 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ソース(抜粋)
8 9 | // 一旦リセットする resetPreview(); |
選択中のファイル情報は変数filesに格納されています。
選択されているファイルの総数は、files.length
で得られます。
したがって、for文を使って for(var i = 0; i < files.length; i++) { ~処理内容~ }
などと書くことで、ファイルの個数分だけループできます。選択中のファイルが1つもない場合は、ループ内部の処理は1度も実行されません。
JavaScriptソース(抜粋)
12 13 14 15 16 17 18 | // i番目のファイル情報を得る var file = files[i]; // 選択中のファイルが画像かどうかを判断 if ( file.type.indexOf( "image" ) < 0 ) { /* 画像以外なら無視 */ continue ; } |
files[i]
でi番目のファイル情報を得て、変数fileに格納しています。
ここでは画像ファイルだけを対象にしてプレビューさせるため、ファイルタイプを調べて「image」という文字列が含まれているかどうかを確認しています。
もし含まれていない場合は、「画像ではない」と判断して次のループへ進みます。
JavaScriptソース(抜粋)
19 20 | // ファイル選択ボタンのラベルに選択個数を表示 document.getElementById( "selectednum" ).innerHTML = (i+1) + "個選択中" ; |
ファイル選択ボタンのラベルには、id名「selectednum」が付加されています。
getElementByIdメソッドとinnerHTMLプロパティを使って、ラベルの表示に選択中のファイル個数を表示させています。
JavaScriptソース(抜粋)
21 22 23 24 25 26 27 | // 画像プレビュー用の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ソース(抜粋)
28 29 30 31 | // 画像を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本くらい自動表示されています。(たぶん)