序章

ドラッグアンドドロップは、多くのグラフィカルユーザーインターフェイスで見られる一般的なユーザー操作です。

ドラッグアンドドロップ機能をアプリに追加するための既存のJavaScriptライブラリがあります。 ただし、ライブラリが利用できない場合や、プロジェクトに必要のないオーバーヘッドや依存関係が生じる場合があります。 このような状況では、最新のWebブラウザーで使用できるAPIの知識により、代替ソリューションを提供できます。

HTMLドラッグアンドドロップAPIは、DOMのイベントモデルに依存して、ドラッグアンドドロップされているものに関する情報を取得し、ドラッグアンドドロップでその要素を更新します。 JavaScriptイベントハンドラーを使用すると、任意の要素をドラッグ可能なアイテムまたはドロップできるアイテムに変えることができます。

このチュートリアルでは、HTMLドラッグアンドドロップAPIとバニラJavaScriptを使用して、イベントハンドラーを使用するドラッグアンドドロップの例を作成します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • ドラッグアンドドロップAPI(Chrome 4以降、Firefox 3.5以降、Safari 3.1以降、Edge 18以降)をサポートする最新のWebブラウザー。

ステップ1—プロジェクトと初期マークアップの作成

私たちのプロジェクトは、2種類の子要素を持つコンテナで構成されます。

  • ドラッグできる子要素
  • 要素をドロップできる子要素

まず、ターミナルウィンドウを開き、新しいプロジェクトディレクトリを作成します。

  1. mkdir drag-and-drop-example

次に、そのディレクトリに移動します。

  1. cd drag-and-drop-example

次に、そのディレクトリにindex.htmlファイルを作成します。

  1. nano index.html

次に、HTMLWebページの定型コードを追加します。

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>My Drag-and-Drop Example</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
  </body>
</html>

そして、<body>タグの間に、draggableアイテムとdropzone(ドロップターゲット)を追加します。

index.html
<div class="example-parent">
  <div class="example-origin">
    <div
      id="draggable-1"
      class="example-draggable"
    >
      draggable
    </div>
  </div>

  <div
    class="example-dropzone"
  >
    dropzone
  </div>
</div>

ファイルを保存して閉じます。 次に、style.cssファイルを作成します。

  1. nano style.css

次に、index.htmlファイルに要素のスタイルを追加します。

style.css
.example-parent {
  border: 2px solid #DFA612;
  color: black;
  display: flex;
  font-family: sans-serif;
  font-weight: bold;
}

.example-origin {
  flex-basis: 100%;
  flex-grow: 1;
  padding: 10px;
}

.example-draggable {
  background-color: #4AAE9B;
  font-weight: normal;
  margin-bottom: 10px;
  margin-top: 10px;
  padding: 10px;
}

.example-dropzone {
  background-color: #6DB65B;
  flex-basis: 100%;
  flex-grow: 1;
  padding: 10px;
}

これにより、アプリにフォーマットが追加されます。 これで、ブラウザでindex.htmlを表示し、draggable<div>dropzone<div>が生成されることを確認できます。

Screenshot of draggable and dropzone divs

次に、draggable属性を追加して、最初の<div>を明示的にドラッグ可能にします。

index.html
<div class="example-parent">
  <div class="example-origin">
    <div
      id="draggable-1"
      class="example-draggable"
      draggable="true"
    >
      draggable
    </div>
  </div>

  <div
    class="example-dropzone"
  >
    dropzone
  </div>
</div>

ファイルを保存して閉じます。

最後に、ブラウザでindex.htmlをもう一度表示します。 draggable <div>をクリックして画面上でドラッグすると、移動していることが視覚的に示されます。

draggable属性のデフォルト値はautoです。 つまり、要素がドラッグ可能かどうかは、ブラウザのデフォルトの動作によって決まります。 通常、これは、draggable="true"を指定せずに、テキストの選択、画像、およびリンクをドラッグできることを意味します。

これで、ドラッグ可能な要素を含むHTMLファイルができました。 oneventハンドラーの追加に進みます。

ステップ2—JavaScriptを使用したドラッグアンドドロップイベントの処理

現在、ドラッグ可能な要素をドラッグしているときにマウスを離しても、何も起こりません。 DOM要素のドラッグアンドドロップでアクションをトリガーするには、ドラッグアンドドロップAPIを利用する必要があります。

  • ondragstart:このイベントハンドラーはdraggable要素にアタッチされ、dragstartイベントが発生したときに発生します。
  • ondragover:このイベントハンドラーはdropzone要素にアタッチされ、dragoverイベントが発生したときに発生します。
  • ondrop:このイベントハンドラーはdropzone要素にもアタッチされ、dropイベントが発生したときに発生します。

注:合計8つのイベントハンドラーがあります:ondragondragendondragenterondragexitondragleaveondragoverondragstart、およびondrop。 この例では、それらすべてを必要としません。

まず、index.html内の新しいscript.jsファイルを参照しましょう。

index.html
<body>
  ...
  <script src="script.js"></script>
</body>

次に、新しいscript.jsファイルを作成します。

  1. nano script.js

DataTransferオブジェクトは、現在発生しているドラッグに関連する情報を追跡します。 ドラッグとドロップで要素を更新するには、DataTransferオブジェクトに直接アクセスする必要があります。 これを行うには、DOM要素のDragEventからdataTransferプロパティを選択します。

注: DataTransferオブジェクトは、同時にドラッグされている複数の要素の情報を技術的に追跡できます。 この例では、1つの要素のドラッグに焦点を当てます。

dataTransferオブジェクトのsetDataメソッドを使用して、現在ドラッグされている要素のドラッグ状態情報を設定できます。 2つのパラメータが必要です。

  • 2番目のパラメーターの形式を宣言する文字列
  • 転送される実際のデータ

私たちの目標は、draggable要素を新しい親要素に移動することです。 一意のidを持つdraggable要素を選択できる必要があります。 ドラッグした要素のidは、後で使用できるようにsetDataメソッドで設定できます。

script.jsファイルに戻り、setDataを使用する新しい関数を作成しましょう。

script.js
function onDragStart(event) {
  event
    .dataTransfer
    .setData('text/plain', event.target.id);
}

注: Internet Explorer 9〜11には、'text/plain'の使用に問題があると報告されています。 そのブラウザのフォーマットは'text'である必要があります。

ドラッグされたアイテムのCSSスタイルを更新するには、DOMイベントを再度使用し、currentTargetに必要なスタイルを設定することでそのスタイルにアクセスできます。

関数に追加して、backgroundColoryellowに変更しましょう。

script.js
function onDragStart(event) {
  event
    .dataTransfer
    .setData('text/plain', event.target.id);

  event
    .currentTarget
    .style
    .backgroundColor = 'yellow';
}

注:ドラッグのみのスタイルが必要な場合は、変更したスタイルをドロップ時に手動で再度更新する必要があります。 ドラッグの開始時に何かを変更した場合、ドラッグした要素は、元に戻さない限り、その新しいスタイルを維持します。

これで、ドラッグを開始するときのJavaScript関数ができました。

index.htmldraggable要素にondragstartを追加できます。

index.html
<div class="example-parent">
  <div class="example-origin">
    <div
      id="draggable-1"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      draggable
    </div>
  </div>

  <div class="example-dropzone">
    dropzone
  </div>
</div>

ブラウザでindex.htmlを表示します。 ここでアイテムをドラッグしようとすると、関数で宣言されたスタイルが適用されます。

Animated gif depicting an element getting dragged but not dropping

ただし、クリックを離しても何も起こりません。

このシーケンスで起動される次のイベントハンドラーはondragoverです。

ブラウザの<div>などの特定のDOM要素のデフォルトのドロップ動作は、通常、ドロップを受け入れません。 この動作は、実装しようとしている動作をインターセプトします。 目的のドロップ動作を確実に得るために、preventDefaultを適用します。

script.jsファイルに戻り、preventDefaultを使用する新しい関数を作成してみましょう。 次のコードをファイルの最後に追加します。

script.js
function onDragOver(event) {
  event.preventDefault();
}

これで、index.htmldropzone要素にondragoverを追加できます。

index.html
<div class="example-parent">
  <div class="example-origin">
    <div
      id="draggable-1"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      draggable
    </div>
  </div>

  <div
    class="example-dropzone"
    ondragover="onDragOver(event);"
  >
    dropzone
  </div>
</div>

この時点では、実際のドロップを処理するコードはまだ作成されていません。 このシーケンスで発生する最後のイベントハンドラーはondropです。

script.jsファイルに戻り、新しい関数を作成しましょう。

dataTransferオブジェクトのsetDataメソッドを使用して、以前に保存したデータを参照できます。 dataTransferオブジェクトのgetDataメソッドを使用します。 私たちが設定したデータはidだったので、それが私たちに返されます。

script.js
function onDrop(event) {
  const id = event
    .dataTransfer
    .getData('text');
}

取得したidを含むdraggable要素を選択します。

script.js
function onDrop(event) {
  // ...

  const draggableElement = document.getElementById(id);
}

dropzone要素を選択します。

script.js
function onDrop(event) {
  // ...

  const dropzone = event.target;
}

draggable要素をdropzoneに追加します。

script.js
function onDrop(event) {
  // ...

  dropzone.appendChild(draggableElement);
}

dataTransferオブジェクトをリセットします。

script.js
function onDrop(event) {
  // ...

  event
    .dataTransfer
    .clearData();
}

これで、index.htmldropzone要素にondropを追加できます。

index.html
<div class="example-parent">
  <div class="example-origin">
    <div
      id="draggable-1"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      draggable
    </div>
  </div>

  <div
    class="example-dropzone"
    ondragover="onDragOver(event);"
    ondrop="onDrop(event);"
  >
    dropzone
  </div>
</div>

これが完了すると、ドラッグアンドドロップ機能が完成します。 ブラウザでindex.htmlを表示し、draggable要素をdropzoneにドラッグします。

Animated gif depicting an element getting dragged and dropped into a drop target

この例では、単一のドラッグ可能なアイテムと単一のドロップターゲットのシナリオを処理します。 複数のドラッグ可能なアイテム、複数のドロップターゲットを作成し、他のすべてのドラッグアンドドロップAPIイベントハンドラーを使用してカスタマイズできます。

ステップ3—複数のドラッグ可能なアイテムを使用して高度な例を作成する

このAPIの使用方法のもう1つの例は、"To-do"列から"Done"列に移動できるドラッグ可能なタスクを含むToDoリストです。

Animated gif depicting multiple To-do tasks being dragged and dropped into a Done column

独自のやることリストを作成するには、一意のidを含むドラッグ可能な要素をindex.htmlに追加します。

index.html
<div class="example-parent">
  <h1>To-do list</h1>
  <div class="example-origin">
    To-do
    <div
      id="draggable-1"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      thing 1
    </div>
    <div
      id="draggable-2"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      thing 2
    </div>
    <div
      id="draggable-3"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      thing 3
    </div>
    <div
      id="draggable-4"
      class="example-draggable"
      draggable="true"
      ondragstart="onDragStart(event);"
    >
      thing 4
    </div>
  </div>

  <div
    class="example-dropzone"
    ondragover="onDragOver(event);"
    ondrop="onDrop(event);"
  >
    Done
  </div>
</div>

ブラウザでindex.htmlを表示し、To-do列の項目をDone列にドラッグします。 To Doアプリケーションを作成し、機能をテストしました。

結論

この記事では、最新のWebブラウザーで使用できるドラッグアンドドロップ機能を調べるためのToDoアプリを作成しました。

ドラッグアンドドロップAPIは、ドラッグアンドドロップ以外のアクションをカスタマイズするための複数のオプションを提供します。 たとえば、ドラッグしたアイテムのCSSスタイルを更新できます。 また、アイテムを移動する代わりに、ドラッグ可能なアイテムをコピーして、ドロップ時に複製されるようにすることもできます。

多くのWebブラウザーはこのテクノロジーをサポートしていますが、オーディエンスがこの機能をサポートしないデバイスで構成されている場合は、このテクノロジーに依存できない可能性があることに注意してください。

ドラッグアンドドロップAPIを使用してドロップできるすべての詳細については、MDNのドキュメントを確認してください。