Tutorial

Vanilla JavaScriptとHTMLを使用してドラッグ&ドロップ要素を作成する方法

JavaScript

はじめに

ドラッグ&ドロップは、多くのGUIで見られる一般的なユーザー操作です。

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

HTML のドラッグ&ドロップ APIは、DOM のイベントモデルに依存して、ドラッグまたはドロップされている内容に関する情報を取得し、ドラッグまたはドロップ時にその要素を更新します。JavaScript イベントハンドラを使用すると、任意の要素をドラッグ可能な項目またはドロップ可能な項目に変換できます。

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

前提条件

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

  • ドラッグ&ドロップ API(Chrome 4以降、Firefox 3.5以降、Safari 3.1以降、Edge 18以降)に対応した最新のWebブラウザ。

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

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

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

まず、端末ウィンドウを開き、新しいプロジェクトディレクトリを作成します。

  • mkdir drag-and-drop-example

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

  • cd drag-and-drop-example

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

  • nano index.html

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

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 ファイルを作成します。

  • 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> が生成されることを確認できるようになりました。

draggable div とdropzone div のスクリーンショット

次に、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ファイルを作成します。

  • nano script.js

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

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

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

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

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

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 を表示します。ここで項目をドラッグしようとすると、関数で宣言されたスタイルが適用されます。

ドラッグされた要素がドロップされないことを示すGIF動画

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

このシーケンスで起動される次のイベントハンドラは、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 の使用例を次に示します。「To-do」列から「Done」列に移動できるドラッグ可能なタスクを含むTo Doリストです。

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

独自の To Do リストを作成するには、一意の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 アプリケーションを作成し、 機能をテストしました。

まとめ

この記事では、To Doアプリケーションを作成し、最新のWebブラウザで使用可能なドラッグ&ドロップ機能を見てきました。

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

多くのWebブラウザではこの技術がサポートされていますが、対象ユーザーがこの機能をサポートしていないデバイスで成り立っている場合は、この技術を利用できない場合があることに注意してください。

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

Creative Commons License