[HTML5] Drag & Drop API おさらい 「ファイルの DnD」
ファイルの Drag & Drop
ここでは、ブラウザ外からファイルを Drag & Drop するケースについて、Drop された画像ファイル(複数可)を全て img 要素として表示することを想定してまとめます。
Drag & Drop 関連イベントの適切な処理
ブラウザ外からのファイルの Drag & Drop は、Drag 中および Drop で送出されるイベントをハンドリングして、適切に処理します。ファイルの Drag & Drop で送出されるイベントは下記の通りです。
イベントターゲット | イベントタイプ | タイミング |
---|---|---|
Drag 中にマウスオーバーしている要素 | dragenter | Drag 操作が要素上へ進入。 |
dragover | Drag 操作が要素上を通過中。 | |
dragleave | Drag 操作が要素上から退出。 | |
Drop 対象の要素 | drop | Drop 実施。 |
このイベントの中でとりあえず処理が必要なものは、Drop 対象要素の「 dragover 」「 drop 」となります。
HTML
<p><div id="target"></div></p>
JavaScript
var target = document.getElementById('target'); function dragOverHandler(event) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; } function dropHandler(event) { event.stopPropagation(); event.preventDefault(); var files = event.dataTransfer.files; Array.prototype.forEach.call(files, function (file) { var reader = new FileReader(); reader.addEventListener('load', function (event) { var img = document.createElement('img'); img.src = event.target.result; target.appendChild(img); }); reader.readAsDataURL(file); }); } target.addEventListener('dragover', dragOverHandler); target.addEventListener('drop', dropHandler);
dragover
function dragOverHandler(event) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; }
- 4 行目
- 「 event.preventDefault() 」は、そのイベントのデフォルト処理をキャンセルするメソッドです。「 dragover 」イベントのデフォルト処理をキャンセルしないと「 drop 」イベントが送出されない為に必須のものとなっています。
- 5 行目
- 「 dataTransfer 」には「 dropEffect 」というプロパティが用意されており、Drop することで何が起こるかを指定することができます 。ここでは「 copy 」を指定することで、イベントターゲットの要素へ Drop した場合に「複製」が行われることを表す「プラスアイコン」がマウスカーソルへ表示されます。
drop
function dropHandler(event) { event.stopPropagation(); event.preventDefault(); var files = event.dataTransfer.files; Array.prototype.forEach.call(files, function (file) { var reader = new FileReader(); reader.addEventListener('load', function (event) { var img = document.createElement('img'); img.src = event.target.result; target.appendChild(img); }); reader.readAsDataURL(file); }); }
- 9, 10 行目
- drop イベントはバブリングする為、最終的にはブラウザへ到達します。ブラウザによっては、その drop イベントで予期せぬ動作をする可能性がある為、イベントの伝播を停止させる「 stopPropagation() 」を実行します。
- また、ブラウザ外からのファイル Drag & Drop では、ブラウザ自身がそのファイルを開こうとしてしまう為、drop イベントのデフォルト処理をキャンセル( preventDefault() )する必要があります。
- 11 行目
- Drop されたファイル(複数可)のサマリは、dataTransfer が持つ files プロパティに FileList オブジェクトとして格納されています。FileList オブジェクトは File オブジェクトの集合です。( NodeList と同様に配列ではありません。)
- 13 行目
- Drag & Drop API と同様に HTML5 で追加された File API である FileReader オブジェクトを生成します。ローカルファイルを読み込む為に使用されます。
- 14 〜 18 行目
- FileReader オブジェクトに load イベントハンドラを指定します。FileReader はファイルの読み込みが完了すると load イベントを送出し、その「 result 」プロパティからファイルデータを取得することができます。ここでは「データ URL 」としてファイルを読み込んでいるので、そのまま img 要素の src 属性へ設定し、HTML へ追加しています。
- 19 行目
- ファイルを「データ URL 」として読み込む為に「 readAsDataURL() 」を実行します。
drop イベントハンドラ内の処理でリストアイテム要素内のデータ(文字列)の移動が完了となります。
視覚情報の付与
Drag & Drop イベントハンドラでスタイルを操作することで、ユーザへ視覚的に Drag & Drop 操作を示すことができます。ユーザビリティ向上の為には押さえておきたいポイントです。
例としては
- dragover 中の要素のスタイルを変更する
- 今マウスボタンを放せばどこに Drop されるかユーザが識別し易くなります。
などでしょうか。
デモ
枠の中に画像ファイルを Drag & Drop するとその画像が小さく表示されます。(複数可)
ここでは Drag & Drop されたデータをそのまま img 要素のソースにして表示しているのでリロードすれば消えてしまいますが、データをサーバへ転送・保存するようにすれば Web アルバムのようになりますね。
まとめ
ファイルの Drag & Drop をまとめると以下のようになります。
Drag & Drop イベントの処理
順番 | イベントタイプ | 処理(太字は必須) |
---|---|---|
1 | dragenter |
|
2 | dragover |
|
3 | dragleave |
|
4 | drop |
|
5 | dragend |
|
Gmail のファイル添付などでも利用されている通り、Web 上のサービスへファイルをアップロードしたいという需要は少なくはないことでしょう。DOM 要素の Drag & Drop も併せて用いれば、ネイティブアプリケーションに引けをとらない操作感を演出できるかもしれませんね。