Drag and Drop API

ドラッグ&ドロップ

ドラッグ&ドロップとは

ドラッグ&ドロップとは、ウェブページ内の要素やローカル環境に保存されたファイルなどのデータを、 マウスで引きずるように移動させて他の場所に置く操作のことです。 HTML5以前にも、mousedownやmouseupなどのイベントで実現することはできましたが、 HTML5ではドラッグ&ドロップ専用の新しいイベントや新しいメソッド・属性が追加されています。

ドラッグ&ドロップを理解するには、ドラッグ操作とドロップ操作を分けて考えると理解しやすいでしょう。 ドラッグ操作は要素などのデータをマウスでつかんで引きずるように動かすこと、 ドロップ操作はその動かしたデータをドロップ先の要素内に配置することです。

HTML側では、ドラッグする要素にdraggable属性を指定する

HTML側では、ドラッグする要素にdraggable属性を指定します。 draggable属性の値には、true・false・値なしのいずれかを指定します。 値を指定しない場合にはデフォルト動作となります。

draggable="true"
要素をドラッグ可能にする
draggable="false"
要素をドラッグ不可にする
値なし
その要素のデフォルト動作となる

href属性が指定されたa要素、および、src属性が指定されたimg要素は、デフォルトでドラッグ可となっています。 それ以外の要素は、デフォルトではドラッグ不可です。

HTML

<div id="apple" draggable="true">りんご</div>

ドロップ先の要素にdropzone属性を指定する

昔はdropzone属性で「ここにドロップできる」を表す考え方もありましたが、現在は主要ブラウザで実用されていません。実務では、dragoverなどでevent.preventDefault()を呼んで「ここにドロップOK」を許可する方法が一般的です。

そのためこのページのサンプルでも、dropzoneは使わずに「ドロップ先の要素でdragoverを受け取り、preventDefault()する」流れで説明します。

※補足:ドラッグ中に「コピー/移動」の見た目を変えたい場合は、DataTransfer.dropEffecteffectAllowed を使って制御します。

JavaScript側では、DataTransferオブジェクトでドラッグデータをドロップ先へ受け渡す

JavaScript側では、ドラッグ開始時にDataTransferというオブジェクトにドラッグするデータをセットしておき、 ドロップ時にそのデータを取り出して、ドラッグデータをドロップ先へ受け渡すことでドラッグ&ドロップを実現します。

この際、データのセットに使用するのがsetData()メソッド、 データの取得に使用するのがgetData()メソッドです。

JavaScript

// データをセットする(dragstart など)
	event.dataTransfer.setData(format, data);

	// データを取得する(drop など)
	data = event.dataTransfer.getData(format);

Drag and Drop API 専用コードまとめ(属性・イベント・DataTransfer)

「何が専用で、どれを使えばいいか」を忘れたときに見返す用の一覧です。

HTML属性(ドラッグできる/できないを決める)

draggable="true"
この要素をドラッグできるようにします(ドラッグ元)。
draggable="false"
この要素をドラッグできないようにします。
draggable(値なし)
要素のデフォルト動作にまかせます(aimgなどはデフォルトでドラッグ可なことがあります)。

イベント(Drag and Drop 専用の7つ)

dragstart
ドラッグ開始時。ここで dataTransfer に渡すデータを入れるのが定番です。
drag
ドラッグ中ずっと発生。回数が多いので重い処理は避けます。
dragenter
ドラッグ中の要素が、ドロップ候補の要素に「入った瞬間」。見た目のハイライトに使われがちです。
dragleave
ドラッグ中の要素が、ドロップ候補の要素から「出た瞬間」。ハイライト解除などに使います。
dragover
ドラッグ中の要素が、ドロップ候補の上に「重なっている間」。ここで event.preventDefault() しないと、基本的にドロップできません。
drop
ドロップした瞬間。getData() で受け取って、DOMに追加したり、処理を実行します。
dragend
ドラッグが終わったとき(成功・失敗どちらでも)。後片付けに使います。

イベント属性(HTMLに直書きする場合)

ondragstart
dragstart をHTML属性で受け取る書き方です。
ondragover
dragover をHTML属性で受け取る書き方です(ドロップ可能にするなら preventDefault() がほぼ必須)。
ondrop
drop をHTML属性で受け取る書き方です。
ondragenter / ondragleave / ondrag / ondragend
各イベントをHTML属性で受け取る書き方です(JSで addEventListener を使う方法もあります)。

専用オブジェクト(データ受け渡しの本体)

event.dataTransferDataTransfer
ドラッグ元→ドロップ先へ「データ」を渡すための箱です。ドラッグ中だけ使えます。
DataTransfer.setData(format, data)
渡したいデータを入れます。初心者向けには "text/plain" が分かりやすいです。
DataTransfer.getData(format)
入れておいたデータを取り出します(setData と同じ format を指定します)。
DataTransfer.clearData([format])
入れたデータを消します(format省略で全部)。

DataTransferのよく使うプロパティ(見た目・許可など)

DataTransfer.dropEffect
ドロップ時の「コピー/移動」などの見た目(カーソル)に関係します。
DataTransfer.effectAllowed
ドラッグ元が「コピー可/移動可」など、許可する操作を指定します。
DataTransfer.types
入っているデータ形式の一覧です(例:"text/plain" など)。

ファイルドロップで使うもの(要素移動とは別系統)

DataTransfer.files
ユーザーが落としたファイル一覧(FileList)です。ファイルドロップを扱うときに使います。
DataTransfer.items
より細かい情報(DataTransferItemList)です。ファイル以外も含むことがあります。

よく一緒に出る「イベント側の基本メソッド」

event.preventDefault()
ドロップを成立させるために重要。特に dragover で呼ぶのが定番です。
event.currentTarget
イベントハンドラを付けた要素。ドロップ先を安定して扱いたいときに便利です。
event.target
実際にマウスが乗っていた一番内側の要素。子要素があると想定外になりやすいです。

HTML5のドラッグ&ドロップで定義されている7つのイベント

ドラッグ&ドロップは、7つのイベントをきっかけにして動きます。イベントが起きたタイミングで DataTransfer の中身を入れたり取り出したりして、ドロップ先の動きをJavaScriptで決めるイメージです。

ポイントは「イベント名を暗記する」よりも、どんな順番で起きるかをつかむことです。

dragstart(つかんだ瞬間)
ドラッグ開始。ここで event.dataTransfer.setData() を使って「運ぶ中身」を入れておくのが定番です。
drag(運んでいる最中)
ドラッグ中に何度も発生します。回数が多いので、重い処理はしない方が安全です。
dragenter(ドロップ先に入った瞬間)
ドロップ候補の上に入った瞬間に発生します。ここで見た目を変えて「ここに落とせるよ」を表現すると分かりやすいです。
dragover(ドロップ先の上にいる間)
重要:ここで event.preventDefault() しないと、基本的にドロップできません(ブラウザの既定動作で拒否されます)。
dragleave(ドロップ先から出た瞬間)
ドロップ候補の外に出た瞬間に発生します。ハイライト解除などに使います。
drop(落とした瞬間)
ドロップ成立。event.dataTransfer.getData() で中身を取り出して、要素を追加したり処理を実行します。
dragend(終わったとき)
成功・失敗に関係なくドラッグ終了時に発生します。後片付け(見た目を戻す等)に使います。

迷ったら、まずは dragstart / dragover / drop の3つを押さえると動かせます。

ドラッグ操作の概要

ドラッグ操作では、ドラッグするデータをDataTransferオブジェクトにセットすることで、ドロップ先に受け渡すことのできる状態にします。

JavaScript

// ドラッグ開始時の処理
	function f_dragstart(event){
		//ドラッグするデータのid名をDataTransferオブジェクトにセット
		event.dataTransfer.setData("text/plain", event.target.id);
	};

Sample

色付きのボックスを左のボックスから右のボックスに移動できます。

HTML

<div id="box1">
		<div id="item" draggable="true" ondragstart="drag(event)"></div>
	</div>
	<div id="box2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

CSS

#box1, #box2 {
		float: left;
		width: 70px;
		height: 70px;
		border: 1px solid #999;
	}
	#item {
		margin: 3px;
		width: 50px;
		height: 50px;
		background-color: #900;
		border: 1px solid #999;
	}

JavaScript

function allowDrop(ev) {
		ev.preventDefault();
	}

	function drag(ev) {
		ev.dataTransfer.setData("text/plain", ev.target.id);
	}

	function drop(ev) {
		ev.preventDefault();
		let data = ev.dataTransfer.getData("text/plain");
		ev.currentTarget.appendChild(document.getElementById(data));
	}