はじめに
ドラッグ&ドロップは、多くの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
次に、そのディレクトリに移動します。
その後、そのディレクトリにindex.html
ファイルを作成します。
次に、HTML Web ページに定型コードを追加します。
index.html
<!DOCTYPE html>
<html>
<head>
<title>My Drag-and-Drop Example</title>
<link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/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
ファイルを作成します。
次に、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>
を明示的にドラッグ可能にします。
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つのイベントハンドラがあります。ondrag
、ondragend
、ondragenter
、ondragexit
、ondragleave
、ondragover
、ondragstart
、およびondrop
です。この例では、これらすべてを必要とするわけではありません。
まず、index.html
で新しいscript.js
ファイルを参照しましょう。
index.html
<body>
...
<script src="https://www.digitalocean.com/community/tutorials/script.js"></script>
</body>
次に、新しい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
に必要なスタイルを設定することによって、そのスタイルを利用することができます。
関数に追加して、backgroundColor
をyellow
に変更してみましょう。
script.js
function onDragStart(event) {
event
.dataTransfer
.setData('text/plain', event.target.id);
event
.currentTarget
.style
.backgroundColor="yellow";
}
注:ドラッグのみのスタイルが必要な場合は、変更するスタイルをドロップ時に手動で再度更新する必要があります。ドラッグの開始時に何か変更した場合は、ドラッグした要素は元に戻さない限り、新しいスタイルを保持します。
これで、ドラッグを開始するときのJavaScript関数が作成されました。
次のように、index.html
のdraggable
要素に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
を表示します。ここで項目をドラッグしようとすると、関数で宣言されたスタイルが適用されます。
ただし、クリックを離しても何も起こりません。
このシーケンスで起動される次のイベントハンドラは、ondragover
です。
ブラウザの<div>
のような特定のDOM 要素のデフォルトドロップ動作は、通常、ドロップは受け入れません。この動作は、実装しようとしている動作を阻みます。希望するドロップ動作を確実に行うために、 preventDefault
を適用します。
script.js
ファイルに再度アクセスして、preventDefault
を使用するための新しい関数を作成しましょう。ファイルの最後に次のコードを追加します。
script.js
function onDragOver(event) {
event.preventDefault();
}
これで、index.html
の dropzone
要素に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.html
の dropzone
要素に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
にドラッグします。
この例では、単一のドラッグ可能な項目と単一のドロップターゲットのシナリオを処理します。複数のドラッグ可能な項目、複数のドロップターゲットを設定し、他のすべてのドラッグ&ドロップ API イベントハンドラを使用してカスタマイズできます。
ステップ3 — 複数のドラッグ可能な項目を使用して拡張例を作成する
この API の使用例を次に示します。「To-do」
列から「Done」
列に移動できるドラッグ可能なタスクを含むTo Doリストです。
独自の 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 のドキュメント を参照してください。