ファイルのドラッグ & ドロップ

HTML ドラッグ & ドロップインターフェイスは、ウェブアプリケーションでファイルをウェブページにドラッグ & ドロップできるようにするものです。この記事では、基礎となるプラットフォームのファイルマネージャーからドラッグされ、ウェブページにドロップされた 1 つまたは複数のファイルを、アプリケーションで受け入れる方法について記述しています。

ドラッグ & ドロップの主な手順は、ドロップゾーン(ファイルドロップの対象要素)を定義することと、 drop および dragover イベントのイベントハンドラーを定義することです。これらの手順は、コード例を含め、下記で記述します。完全なソースコードは MDN のドラッグ & ドロップリポジトリーで利用できます(プルリクエストや issue を歓迎します)。

なお、 HTML ドラッグ & ドロップでは、ファイルのドラッグ & ドロップに対応するために 2 つの異なる形の API を定義しています。一方の API は DataTransfer インターフェイスで、もう一方の API は DataTransferItemDataTransferItemList インターフェイスです。この例では、両方の API の使用方法を説明します (そして、Gecko 固有のインターフェースは一切使用しません)。

ドロップゾーンの定義

drop イベントの対象となる要素には、 ondrop イベントハンドラーが必要です。以下のコードでは、 <div> 要素を使用してこの処理を行う方法を示しています。

html
<div id="drop_zone" ondrop="dropHandler(event);">
  <p>Drag one or more files to this <i>drop zone</i>.</p>
</div>

通常、アプリケーションでは、ドロップ対象の要素に dragover イベントハンドラーを記述し、そのハンドラーによって、ブラウザーの既定のドラッグ動作をオフにします。このハンドラーを追加するには、 ondragover イベントハンドラーを記載する必要があります。

html
<div
  id="drop_zone"
  ondrop="dropHandler(event);"
  ondragover="dragOverHandler(event);">
  <p>Drag one or more files to this <i>drop zone</i>.</p>
</div>

最後に、アプリケーションはドロップ先の要素のスタイルを設定して、要素がドロップゾーンであることを視覚的に示したい場合があります。この例では、 drop ドロップ先の要素は以下のスタイル設定を使用します。

css
#drop_zone {
  border: 5px solid blue;
  width: 200px;
  height: 100px;
}

メモ: dragstart および dragend イベントは、 OS からブラウザーへファイルをドラッグしているときには発生しません。 OS のファイルがブラウザーへドラッグされてきたことを検出するには、 dragenter および dragleave を使用してください。

ドロップの処理

drop イベントは、ユーザーがファイルをドロップしたときに発行されます。以下のドロップハンドラーでは、ブラウザーが DataTransferItemList インターフェイスを使用している場合は getAsFile() メソッドを使用して各ファイルにアクセスし、そうでない場合は DataTransfer インターフェイスの files プロパティを使用して各ファイルにアクセスしています。

この例では、ドラッグしたそれぞれのファイル名をコンソールに書き出す方法を説明します。実際のアプリケーションでは、 File API を使用してファイルを処理したいかもしれません。

この例では、ファイルでないドラッグアイテムは無視されることに注意してください。

js
function dropHandler(ev) {
  console.log("File(s) dropped");

  // 既定の動作で防ぐ(ファイルが開かれないようにする)
  ev.preventDefault();

  if (ev.dataTransfer.items) {
    // DataTransferItemList インターフェイスを使用して、ファイルにアクセスする
    [...ev.dataTransfer.items].forEach((item, i) => {
      // ドロップしたものがファイルでない場合は拒否する
      if (item.kind === "file") {
        const file = item.getAsFile();
        console.log(`… file[${i}].name = ${file.name}`);
      }
    });
  } else {
    // DataTransfer インターフェイスを使用してファイルにアクセスする
    [...ev.dataTransfer.files].forEach((file, i) => {
      console.log(`… file[${i}].name = ${file.name}`);
    });
  }
}

ブラウザー既定のドラッグ動作を阻止する

以下の dragover イベントハンドラーは preventDefault() を呼び出して、ブラウザーの既定のドラッグ & ドロップハンドラーをオフにしています。

js
function dragOverHandler(ev) {
  console.log("File(s) in drop zone");

  // 既定の動作で防ぐ(ファイルが開かれないようにする)
  ev.preventDefault();
}

関連情報