【JavaScript】dragstart / dragover / drop – 要素や選択されたテキストをドラッグし始めたときに発生するイベント

ドラッグ&ドロップ(Drag and Drop)は、ユーザーが直感的に要素を移動できるインタラクションを実現するために使用されます。dragstartdragoverdropなどのイベントを組み合わせることで、アイテムの移動やファイルアップロードといった多様な操作をサポートできます。

dragstart / dragover / dropイベントとは?

ドラッグ&ドロップは以下のような流れで処理されます。

dragstartイベント

  • ドラッグ操作が開始されたときに発生します。
  • データ転送の設定(例: テキストや画像)や、ドラッグされる要素のスタイル変更を行います。

dragoverイベント

  • 要素がドラッグ可能な対象(ドロップゾーン)の上にある間、継続的に発生します。
  • デフォルトの動作を防ぐ(event.preventDefault())ことで、要素がそのゾーンにドロップ可能かを指定します。

dropイベント

  • 要素がドロップゾーンにドロップされたときに発生します。
  • ドロップされたデータの取得や処理を行います。

基本的な使い方

以下は、四角形を別のエリアにドラッグ&ドロップするシンプルな実装例です。

See the Pen dragstart / dragover / drop by tones (@tonescodedesign) on CodePen.

JavaScript
const draggable = document.getElementById('draggable');
const dropzone = document.getElementById('dropzone');

// ドラッグ開始時
draggable.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', 'This is a draggable element.');
  draggable.style.opacity = '0.5';
  console.log('ドラッグ開始');
});

// ドラッグ終了時
draggable.addEventListener('dragend', () => {
  draggable.style.opacity = '1';
  console.log('ドラッグ終了');
});

// ドロップゾーンの上にいる間
dropzone.addEventListener('dragover', (event) => {
  event.preventDefault(); // デフォルト動作を防ぐ(ドロップを許可)
  dropzone.classList.add('over');
  console.log('ドラッグオーバー中');
});

// ドロップゾーンを離れたとき
dropzone.addEventListener('dragleave', () => {
  dropzone.classList.remove('over');
  console.log('ドラッグエリア外に出た');
});

// ドロップ時
dropzone.addEventListener('drop', (event) => {
  event.preventDefault();
  const data = event.dataTransfer.getData('text/plain');
  dropzone.textContent = `ドロップされました: ${data}`;
  dropzone.classList.remove('over');
  console.log('ドロップ成功');
});
HTML
<div id="draggable" class="draggable" draggable="true">ドラッグ</div>
<div id="dropzone" class="dropzone">ここにドロップ</div>
CSS
.draggable {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 100px;
  background-color: skyblue;
  margin: 20px;
  cursor: grab;
}
.dropzone {
  width: 300px;
  height: 200px;
  border: 2px dashed gray;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 20px;
}
.dropzone.over {
  border-color: green;
}

dragstart、dragover、dropの詳細

dragstartイベント

  • ドラッグ操作の開始を検知します。
  • event.dataTransferを使用して、ドラッグ中に転送するデータを設定可能です。
JavaScript
element.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text', 'ドラッグデータ');
  console.log('ドラッグが開始されました');
});

dragoverイベント

  • 要素がドロップゾーンの上にある間に継続して発生します。
  • event.preventDefault()を必ず呼び出す必要があります(呼び出さないとドロップが無効化される)。
JavaScript
dropzone.addEventListener('dragover', (event) => {
  event.preventDefault();
});

dropイベント

  • ドラッグされた要素がドロップゾーン内にドロップされたときに発生します。
  • ドロップデータを取得して処理を実行します。
JavaScript
dropzone.addEventListener('drop', (event) => {
  event.preventDefault();
  const data = event.dataTransfer.getData('text');
  console.log('ドロップされたデータ:', data);
});

サンプルコード

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

以下は、ファイルをドラッグ&ドロップでアップロードする実装例です。

See the Pen dragstart / dragover / drop by tones (@tonescodedesign) on CodePen.

JavaScript
const dropzone = document.getElementById('dropzone');

dropzone.addEventListener('dragover', (event) => {
  event.preventDefault();
  dropzone.classList.add('over');
});

dropzone.addEventListener('dragleave', () => {
  dropzone.classList.remove('over');
});

dropzone.addEventListener('drop', (event) => {
  event.preventDefault();
  dropzone.classList.remove('over');

  const files = event.dataTransfer.files;
  console.log('ドロップされたファイル:', files);

  if (files.length > 0) {
    Array.from(files).forEach(file => {
      console.log(`ファイル名: ${file.name}, サイズ: ${file.size}バイト`);
    });
  }
});
HTML
<div id="dropzone" class="dropzone">ここにファイルをドロップしてください</div>
CSS
.dropzone {
  width: 300px;
  height: 200px;
  border: 2px dashed gray;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 20px auto;
  text-align: center;
}
.dropzone.over {
  border-color: green;
  background-color: #f0fff0;
}

使用時の注意点

event.preventDefault()の重要性

dragoverdropイベントでデフォルト動作を防ぐことが必須です。これを行わないと、ブラウザがデフォルトのドラッグ&ドロップ動作を処理します(例: ファイルを開くなど)。

draggable属性の指定

ドラッグ可能な要素にはdraggable="true"を明示的に設定します。

dataTransferオブジェクトの利用

ドラッグ中にデータを転送するためのメカニズムを提供します。setDataでデータを設定し、getDataで取得します。

アクセシビリティ

  • キーボード操作でも同等の機能を提供するように配慮してください。
  • スクリーンリーダーで適切に動作するように、aria-describedbyaria-grabbed属性を利用します。
  • 視覚的な変化だけではなく、音声やテキストでも状態の変化を通知しましょう。
HTML
<div id="dropzone" role="button" aria-label="ドラッグアンドドロップのドロップエリア">
    ここにドラッグしてドロップしてください。
</div>