HTML ドラッグ & ドロップ API を使うことで、ファイルのアップロードやリストの並び替えなどが可能になります。今回はドラッグ & ドロップのファイルアップローダーを作る際に、PC ローカルからドラッグされているのか、HTML ページ内部の要素をドラッグしているのかを判定する方法を紹介します。

早速コードです。

<script>
function dragover_handler(event) {
  event.preventDefault();
  if(event.dataTransfer.types[0] === 'Files') {
    console.log('drop from PC (desktop or Finder etc)');
  } else {
    console.log('drop from HTML');
  }
}
function drop_handler(event) {
  event.preventDefault();
  // 実際のドロップ後の処理
}
</script>
<div ondrop="drop_handler(event)" ondragover="dragover_handler(event)">Drop Zone</div>

判定するタイミングはドロップ完了時点の ondrop ではなく、手前の ondragover で判定させます。なぜかと言うと、多くのドラッグインターフェイスは、ドラッグ中にエリアの上にポインタが重なった時に、ドロップエリアのスタイルを変更することが多いですが、ドロップ対象じゃないものの場合にはスタイルを変更させたくないからです。

ローカルからのファイルドラッグは常に Files になる

ドラッグイベントオブジェクトには dataTransfer が存在しています。ここからドロップするファイル情報が読み取れます。そこで dataTransfer.types プロパティを見て、ドラッグしている要素のタイプを取得します。

ローカルからドラッグしている場合は、どんなファイルであれ文字列の Files だけが格納されています。というわけで、この Files が格納されている場合はローカルからファイルをドラッグしていると判定が可能です。

また、HTML 内部のドラッグ可能な要素、例えば画像をドラッグすると ["text/uri-list","text/html"] というタイプになります。テキストだと ["text/plain","text/html"] です。ドラッグ中のタイミングだと HTML 内部のリソースであっても完全なファイル情報は読み取れません。

完全なファイル情報を読み取るにはドロップが完了した ondrop のタイミングでないとダメです。

そもそもドラッグさせない

他の対処方法として、HTML 内部のリソースをドラッグ不可能な要素にすることもできます。

<img src="./hoge.png" draggable="false">

しかし、ケースによっては全ての HTML 要素に指定しないといけない場合もあるので、ドロップエリアで判定ができるならそうした方がいい場合もあると思います。