ちょっと案件でファイルをHTML上で読み込む必要があり、ファイルのドラッグ&ドロップについて調べていたんですが、なんか微妙に分かりづらい説明が目についたので備忘録的にまとめます。
試しに画像ファイルをドラッグ&ドロップで読み込んで表示するサンプルを作ったのでこれを元に説明を。
サンプルページ(武田制作のサイト上に保存してます)
サンプルは上部のドラッグ&ドロップエリアに画像ファイルをドロップすると読み込んで下にプレビュー表示します。
ちょっとコード全体を貼った上で順番に解説します。
<サンプルコード全体>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <title>画像ファイルドラッグ&ドロップ読込テスト</title> <style> body { margin: 16px; padding: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; } h3 { margin: 0 0 0.5em 0; padding: 0; } h4 { margin: 1em 0 0.5em 0; padding: 0; } #fileDropArea { width: 480px; height: 240px; border: 2px solid #888888; border-radius: 8px; background-color: #eeeeee; display: flex; justify-content: center; align-items: center; } #viewLoadedImage { display: block; width: 480px; height: auto; min-height: 240px; border: 1px solid #cccccc; } </style> </head> <body> <h3>ファイルドロップ読込テスト</h3> <!-- ドロップエリア --> <div id="fileDropArea"> <p>ここに画像ファイルをドロップ</p> </div> <!-- 読込画像表示 --> <h4><読込画像></h4> <img src="" id="viewLoadedImage"> <!-- Script --> <script> // 読み込み画像プレビュー用imgを控えておく var _viewLoadedImg = document.getElementById("viewLoadedImage"); // FileReaderオブジェクトを用意しておく var _fileReader = new FileReader(); // FileReader読み込み完了時のイベントをセットしておく _fileReader.addEventListener("load", () => { _viewLoadedImg.src = _fileReader.result; }, false); function IsDragOver(event) { // --- DragOver中の処理 --- // ドラッグ中のブラウザ既定処理を抑制 event.preventDefault(); } function FileDrop(event) { // --- ファイルドロップ時の処理 --- // ドロップ時のブラウザ既定処理を抑制 event.preventDefault(); // ファイル確認 ※一応両方にデータ乗ってるっぽい。両方ともFileオブジェクト let _file = null; if (event.dataTransfer.items) { // 配列先頭のファイルオブジェクトを取得 _file = event.dataTransfer.items[0].getAsFile(); } else { // 配列先頭のファイルオブジェクトを控える _file = event.dataTransfer.files[0]; } // ファイルをDataURL(Base64)で読込 _fileReader.readAsDataURL(_file); } // DOMが全部読み込まれた時のイベントを設定 document.addEventListener("DOMContentLoaded", function() { // fileDropArea に各イベントハンドラを設定 document.getElementById("fileDropArea").addEventListener("dragover", IsDragOver); document.getElementById("fileDropArea").addEventListener("drop", FileDrop); }); </script> </body> </html>
<HTML基本部分>
画面を構成する要素ですが、主にドロップエリア用のDIVと画像プレビュー用のIMG(ブロック化)で構成しています。fileDropAreaがドロップエリアで、viewLoadedImageが画像プレビューエリアになります。
fileDropAreaはFlexBoxで文字が中央寄せになるようにしている程度なのですが、viewLoadedImageの方は、縦幅をautoにする事で画像の横幅のみ固定して縦は画像の比率に合わせて伸縮するように設定しています。
<style> body { margin: 16px; padding: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; } h3 { margin: 0 0 0.5em 0; padding: 0; } h4 { margin: 1em 0 0.5em 0; padding: 0; } #fileDropArea { width: 480px; height: 240px; border: 2px solid #888888; border-radius: 8px; background-color: #eeeeee; display: flex; justify-content: center; align-items: center; } #viewLoadedImage { display: block; width: 480px; height: auto; min-height: 240px; border: 1px solid #cccccc; } </style>
HTML
<h3>ファイルドロップ読込テスト</h3> <!-- ドロップエリア --> <div id="fileDropArea"> <p>ここに画像ファイルをドロップ</p> </div> <!-- 読込画像表示 --> <h4><読込画像></h4> <img src="" id="viewLoadedImage">
<ドロップした画像を読み込んで表示するまでの処理について>
ここからがキモなんですけど、主にスクリプトで処理を行っています。順番に解説すると…
// 読み込み画像プレビュー用imgを控えておく var _viewLoadedImg = document.getElementById("viewLoadedImage");
初めに読み込んだ画像をプレビューする為のimg要素の参照を予めとっておいて、
// FileReaderオブジェクトを用意しておく var _fileReader = new FileReader(); // FileReader読み込み完了時のイベントをセットしておく _fileReader.addEventListener("load", () => { _viewLoadedImg.src = _fileReader.result; }, false);
ファイル取得後の読込に使うFileReaderオブジェクトを作っておきます。
そしてファイル読込完了時にFileReaderのloadイベントが走った時に、Imgのsrcにロードされた画像(Base64)が自動的に代入されるようにイベントを設定しておきます。
function IsDragOver(event) { // --- DragOver中の処理 --- // ドラッグ中のブラウザ既定処理を抑制 event.preventDefault(); }
これはドラッグ中にブラウザが反応するのを抑制するだけの関数です。あとでdragoverイベントにハンドルしておきます。
function FileDrop(event) { // --- ファイルドロップ時の処理 --- // ドロップ時のブラウザ既定処理を抑制 event.preventDefault(); // ファイル確認 ※一応両方にデータ乗ってるっぽい。両方ともFileオブジェクト let _file = null; if (event.dataTransfer.items) { // 配列先頭のファイルオブジェクトを取得 _file = event.dataTransfer.items[0].getAsFile(); } else { // 配列先頭のファイルオブジェクトを控える _file = event.dataTransfer.files[0]; } // ファイルをDataURL(Base64)で読込 _fileReader.readAsDataURL(_file); }
ドロップ確定時に走らせる処理です。
event.preventDefault();はブラウザ既定処理(勝手に読み込んで別タブで開くやつ)の抑制用です。
解説サイトの内容だと、ブラウザか処理内容次第でevent.dataTransfer.itemsかevent.dataTransfer.filesのどちらかでFileオブジェクトを取る事になるようで、一応両方動くようにしていますが、log出して確認した所だと両方ともFileオブジェクトが入ってるようでした。(最終的に取れるものも同じ)
どちらも配列で取れるようなので[0]で一番最初の要素だけ拾ってます。
そしてFileオブジェクトが取れたらFileReaderのreadAsDataURL(_file)でDataURL(Base64)形式で読み込んでます。
読み込み完了(loadイベント)したら先に仕込んでいたイベントハンドラでimg要素のsrcに代入されます。
// DOMが全部読み込まれた時のイベントを設定 document.addEventListener("DOMContentLoaded", function() { // fileDropArea に各イベントハンドラを設定 document.getElementById("fileDropArea").addEventListener("dragover", IsDragOver); document.getElementById("fileDropArea").addEventListener("drop", FileDrop); });
ここはDOM要素の読込が完了したタイミングでドロップエリアにdragoverとdropイベントを設定する処理です。
処理の流れは、doragoverとdropでブラウザの処理が走らないようにした上で、dropイベントで読込処理を実行、Fileオブジェクトが取れたらFileReaderオブジェクトで中身を読み込む。画像データがDataURL(Base64)の状態ならsrcに入れるだけで表示されるのでそれで表示、という感じです。
実際に使う時は、ファイル内容の判定、複数同時ドロップされた時の処理など、細々した処理が必要になると思いますが、とりあえずとっかかりまで…