HTML 5では、ドラッグ&ドロップを直接サポートするためのAPIが整備された。以前はmousedown、mousemove、mouseupを利用してドラッグ&ドロップを実現していたが、ブラウザによるネイティブサポートが実現した事で、他のアプリケーションともデータをやり取りできるようになり、コーディングも簡略化された。

HTML 5でドラッグ&ドロップを実現するための最小限の手順は以下の通りだ。

  1. ドラッグ対象となる要素に「draggable="true"」という属性値をセットする。これにより、対象の要素がドラッグ可能となる。ちなみに、img要素やa要素(hrefの指定が必要)はデフォルトでドラッグ可能である
  2. ドラッグ&ドロップ関連のイベントを処理するようコードを記述する。以下のようなイベントが存在する
イベント名 イベントの通知先 説明
dragstart ドラッグ対象の要素 ドラッグが開始された
drag ドラッグ対象の要素 ドラッグ中
dragenter ドラッグ中にマウスオーバーした要素 ドラッグ操作が要素の範囲内に入った
dragover ドラッグ中にマウスオーバーした要素 ドラッグ操作が要素の範囲内を通過中
dragleave ドラッグ中にマウスオーバーした要素 ドラッグ操作が要素の範囲内を出た
drop ドロップ先の要素 ドロップされた
dragend ドラッグ対象の要素 ドラッグが終了した

では、この手順に則ってドラッグ&ドロップを実現したサンプルを以下に示す。以下のサンプルは、「ドラッグしてね!」と表示されているdiv要素を、その下にあるdiv要素に対してドラッグ&ドロップすることができる。ドラッグされるたびに、下のdiv要素には「Hello」という文字列が追加される。

リスト drag-and-drop1.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ドラッグ&ドロップのデモ</title>
<script type="text/javascript">
function init() {
  var source = document.getElementById("dragme");
  var dest = document.getElementById("text");
  // (1) ドラッグ開始
  source.addEventListener("dragstart", function(ev) {
    // dataTransferオブジェクトに対し、データを追加
    var dt = ev.dataTransfer;
    dt.setData("text/plain", "Hello");
    return true;
  }, false);
  // (2) ドラッグオーバー
  dest.addEventListener("dragover", function(ev) {
    // デフォルトの動作(ドロップの拒否)を行わない
    ev.preventDefault();
    return false;
  }, false);
  // (3) ドロップ
  dest.addEventListener("drop", function(ev) {
    // DataTransferオブジェクトからデータを取得
    var dt = ev.dataTransfer;
    var text = dt.getData("text/plain");
    dest.textContent += text;
    // イベントのバブリングを停止
    ev.stopPropagation();
    return false;
  }, false);
}
</script>
</head>
<body onload="init()">
<h1>ドラッグ&ドロップのデモ</h1>
<!-- (4) draggable属性をtrueに -->
<div id="dragme" draggable="true" style="-webkit-user-drag: element; width: 200px; border: 1px solid gray;">
  ドラッグしてね!
</div>
<div id="text"
  style="width: 200px; height: 200px; border: 1px solid gray;"></div>
</body>
</html>

上のソースコードにおけるポイントを解説する。

(1) ドラッグが開始(dragstartイベント)された際、ドロップするデータをDataTransferオブジェクトにセット(setData()メソッド)する。DataTransferオブジェクトとは、ドラッグイベントのdataTransferプロパティから取得することができ、ドラッグ&ドロップによって持ち運ばれるデータを格納するためのオブジェクトだ。同オブジェクトのsetData()メソッドは、第1引数にデータの種別を表す文字列、第2引数にデータ自体を指定する。第1引数のデータ種別には任意の文字列を指定できるだけではなく、「text/plain」や「text/html」といったMIMEタイプも指定することができる。

(2) ドロップを受け付ける要素に対しては、dragoverイベント内で「イベントオブジェクト.preventDefault()」を呼び出す必要がある。これは、dragoverイベントのデフォルト動作が「ドロップ拒否」になっているため。このデフォルト動作を停止しないと、ドロップを受け付けることができない。

(3) ドロップされたら、DataTransferオブジェクトからデータを取得(getData()メソッド)して処理を行う。getData()の引数には、setData()時に指定したデータ種別(ここでは「text/plain」)を指定する。

(4) ドラッグ可能な要素には「draggable="true"」を指定する必要がある。またこのサンプルでは、現在のSafariやChromeでも動作するように、"-webkit-user-drag: element;"というWebkit固有のCSSプロパティを指定している。

さらにこのサンプルは、データ種別として「text/plain」というMIMEタイプを使用しているため、同MIMEタイプを解釈できる他のアプリケーションともデータのやり取りが可能だ。

他のアプリともデータのやり取りが可能(画像はSafariから文字列をドラッグしている)

ドラッグ&ドロップ処理で使用される一般的なMIMEタイプとしては以下のようなものが挙げられる。

  • text/plain … テキストデータ
  • text/html … HTML文字列
  • text/xml … XML文字列
  • text/uri-list … URLのリスト。各URLは改行で区切られる

残念ながら、OSネイティブのファイルをドラッグ&ドロップで取得するための汎用的な方法は存在しない。

ドラッグ&ドロップに関するその他のAPI

ドラッグ&ドロップに関するその他のAPIを駆使すれば、ドラッグ中のイメージをカスタマイズしたり、特定の操作(コピー/移動/リンクなど)以外はドロップを受け付けないようにするなど、さらに高度な処理を実現可能だ。そうした処理を実現するため、DataTransferオブジェクトに用意されているAPIを簡単に挙げておく

属性/メソッド 説明
DOMString dropEffect ドロップ操作の種別を表す。明示的に値をセットする事も可能。ドロップ先がこの種別のドロップを許可していなかったら、ドロップが受け付けられない。指定できる値(文字列)はnone、copy、link、 moveのいずれか。
DOMString effectAllowed ドロップを受け付ける操作の種別を表す。指定できる値(文字列)はnone、copy、copyLink、copyMove、link、linkMove、move、all、uninitializeのいずれか。
DOMStringType types 格納されているデータの種別を、文字列の擬似配列として返す
void clearData(DOMString format) データをクリアする。引数formatを省略すると、すべてのデータがクリアされる
void setData(DOMString format, DOMString data) データをセットする
DOMString getData(DOMString format) データを取得する
void setDragImage(Element image, long x, long y) ドラッグ中のイメージをimg要素でセットする(ブラウザによってはcanvasなども受け付けられる)
void addElement(Element element) ドラッグ対象の要素を追加する

まとめ

今回は「HTML 5 - HTMLとXHTML向けのボキャブラリと関連API」から、HTML 5で新しく導入されたタグやAPIを中心にお伝えした。次回はアプリケーションキャッシュやWeb Storageなど、次世代のWebアプリケーションを実現するための各種APIを解説したいと思う。