マウスドラッグ、マウスドロップ時の dataTransferプロパティ について
前回の投稿では、マウスでDOM要素をドラッグ&ドロップすることを試してみました。
このドラッグ&ドロップには単にDOM要素をドラッグして、特定のDOM要素にドロップするという
動作だけではなく、ドラッグ時、ドロップ時にデータを転送する。という利用方法があります。
このデータを転送する処理については
移動 コピー リンク
という諸方法があり、ドロップ時にそれらの方法を選択した上で処理を決定する。
という方法がアプリケーションの質を向上させるポイントにもなります。
また、最近のブラウザではドラッグ&ドロップを標準化したAPIも整備されてきました。
次の例では、ドラッグ時とドロップ時に、データ転送をする方法として
dataTransferプロパティを使った例を試してみます。
dataTransferプロパティは、DataTransferオブジェクトのプロパティです。
dataTransferを試してみる
画面上のとあるDOM要素をドラッグする際、dragstartイベントが発動されます。
このイベントハンドラの実行時に、dataTransferプロパティに対して、データをセットする
ことができます。
前回の投稿を例にすると、
// ドラッグ開始したとき
element_drag_dom.addEventListener('dragstart', function(e){
display_status('DRAG DOM : dragstart', 'drag_c');
});
上記の箇所の「ドラッグを開始したとき」の dragstart イベントの箇所です。
このイベントの処理に対して、引数のイベントターゲットに、dataTransferプロパティの
setDataメソッドを使って、値を設定します。
具体的には、以下のように記述します。
// ドラッグ開始したとき
element_drag_dom.addEventListener('dragstart', function(e){
display_status('DRAG DOM : dragstart', 'drag_c');
e.dataTransfer.setData("text/plain" , e.target.textContent);
e.stopPropagation();
});
上記サンプルの「dataTransfer.setData」の箇所がデータをセットしている箇所です。
この「setData」メソッドは、第一引数にデータタイプを指定し、第二引数に格納するデータを指定します。
上記の例でいうと、データタイプは text/plain 、データは e.target.textContent(ドラッグ中のDOM要素のテキスト「ドラッグ用DOM」)です。
上記のdataTransferプロパティに文字列が格納されている前提として、
今度はドロップ側のDOM要素に対して、dropイベントの中でdataTransferプロパティにgetDataメソッドを使って
中の値を取り出すことができます。
前回のドロップ時の処理は以下のように書きました。
// ドラッグしている要素をドロップした時
element_drop_dom.addEventListener('drop', function(e){
e.preventDefault();
display_status('DROP DOM : drop', 'drop_c');
// ドラッグ用DOMを削除する
element_drag_dom.parentNode.removeChild(element_drag_dom);
});
このドロップ時の処理に、「let droptxt = e.dataTransfer.getData(“text/plain”);」を追記し、
dataTransferの値を取り出します。
取り出した値がわかりやすいように、一旦droptxtの変数に格納し、
その変数を「element_drop_dom.innerHTML = droptxt;」に出力します。
処理としては、次のように変更しました。
// ドラッグしている要素をドロップした時
element_drop_dom.addEventListener('drop', function(e){
e.preventDefault();
display_status('DROP DOM : drop', 'drop_c');
// ドラッグ用DOMを削除する
element_drag_dom.parentNode.removeChild(element_drag_dom);
let droptxt = e.dataTransfer.getData("text/plain");
element_drop_dom.innerHTML = droptxt;
});
このように書くことにより、ドラッグ用のDOMをドロップ領域用のDOMにドロップしたタイミングで
ドラッグ時にdataTransferに格納した文字列が、ドロップ時にドロップ領域用のDOMに出力されることが確認できます。

この初期画面の右下のドロップ要素のDOMの文字列が

この文字列に変更されます。
HTMLの全体は以下のようになります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style type="text/css">
#drag_dom {
position: absolute;
height: 100px;
width: 150px;
top: 10px;
left: 10px;
background-color: #c0e5ff;
}
#drop_dom {
position: absolute;
height: 200px;
width: 300px;
top: 300px;
left: 500px;
padding: 8px;
border: 5px solid;
border-color: #ffbaba;
}
#drop_dom_hover {
background-color: #998877;
}
#dnd_status_area {
position: absolute;
top: 120px;
}
.drag_c {
color: #28a4fb;
}
.drop_c {
color: #ff5050;
}
</style>
</head>
<body>
<div id="drag_dom" draggable="true">
ドラッグ用DOM
</div>
<div id="drop_dom">
ドロップ領域用DOM
</div>
<div id="dnd_status_area">
<div>ドラッグ&ドロップの状態【日時 - ステータス】</div>
<div id="dnd_status"></div>
</div>
<script type="text/javascript">
// ドラッグするDOM要素を取得
let element_drag_dom = document.getElementById("drag_dom");
// ドロップするDOM要素を取得
let element_drop_dom = document.getElementById("drop_dom");
// ステータス表示用配列を用意
let status_queue = [];
let status_i = 0;
//------------------------------------------------
// ドラッグ対象に登録するイベントハンドラ
//------------------------------------------------
// 要素をドラッグしている時
element_drag_dom.addEventListener('drag', function(e){
display_status('DRAG DOM : drag', 'drag_c');
// ドラッグ中は、文字列を変更
element_drag_dom.innerHTML = "ドラッグ中です";
});
// ドラッグ開始したとき
element_drag_dom.addEventListener('dragstart', function(e){
display_status('DRAG DOM : dragstart', 'drag_c');
e.dataTransfer.setData("text/plain" , e.target.textContent);
e.stopPropagation();
});
// ドラッグが終了した時
element_drag_dom.addEventListener('dragend', function(e){
display_status('DRAG DOM : dragend', 'drag_c');
// ドラッグが終わったら、文字列、cssを戻す
element_drag_dom.innerHTML = "ドラッグ用DOM";
element_drop_dom.style.backgroundColor = "#ffffff";
});
//------------------------------------------------
// ドロップ対象に登録するイベントハンドラ
//------------------------------------------------
// ドラッグ中の要素がドロップできる場所の上に入ったとき
element_drop_dom.addEventListener('dragenter', function(e){
display_status('DROP DOM : dragenter', 'drop_c');
});
// dragexitは「dragleave」と同義イベントなので、ここではコメントアウト
//element_drop_dom.addEventListener('dragexit', function(e){
// display_status('dragexit');
//});
// ドラッグしている要素がドロップできる場所から離れたとき
element_drop_dom.addEventListener('dragleave', function(e){
display_status('DROP DOM : dragleave', 'drop_c');
element_drop_dom.style.backgroundColor = "#ffffff";
});
// ドラッグ中の要素がドロップできる場所の上にあるとき
element_drop_dom.addEventListener('dragover', function(e){
e.preventDefault();
display_status('DROP DOM : dragover', 'drop_c');
// ドロップできる場所の上にあるとき、メッセージとcssを変更する
element_drag_dom.innerHTML = "離すと消えるよ";
element_drop_dom.style.backgroundColor = "#ff5050";
});
// ドラッグしている要素をドロップした時
element_drop_dom.addEventListener('drop', function(e){
e.preventDefault();
display_status('DROP DOM : drop', 'drop_c');
// ドラッグ用DOMを削除する
element_drag_dom.parentNode.removeChild(element_drag_dom);
let droptxt = e.dataTransfer.getData("text/plain");
element_drop_dom.innerHTML = droptxt;
});
/**
* 処理ステータスを画面出力する
*/
function display_status(status, dd_class)
{
// 画面上のDOM要素を取得
let dnd_status = document.getElementById("dnd_status");
// 現在日時を取得
let date_tmp = new Date();
let date_disp = date_tmp.getFullYear() + "/"
+ ('0' + (date_tmp.getMonth() + 1)).slice(-2) + "/"
+ ('0' + date_tmp.getDate()).slice(-2) + " "
+ ('0' + date_tmp.getHours()).slice(-2) + ":"
+ ('0' + date_tmp.getMinutes()).slice(-2) + ":"
+ ('0' + date_tmp.getSeconds()).slice(-2) + "."
+ ('00' + date_tmp.getMilliseconds()).slice(-3);
// ステータスを10個づつの表示用配列へ格納する
status_queue[status_i] = status;
status_i++;
if (status_i >= 30) {
status_i = 0;
}
// キューの内容を画面へ描画する
dnd_status.innerHTML = '';
for (let i = 0; i < status_queue.length; i++) {
dnd_status.innerHTML += date_disp + " - <span class='" + dd_class + "'>" + status_queue[i] + "</span><br />";
}
}
</script>
</body>
</html>
画面にアクセスして、ドラッグ用DOMをドロップ領域用のDOMへドラッグ&ドロップすると、
ドロップ側のDOMの表示が変わることが確認できます。
dataTransferプロパティに格納できるデータについて
dataTransferプロパティに格納できるデータは文字列だけはないです。
例えば、ブラウザ経由でファイルがドロップされた場合はdataTransfer.filesプロパティにファイル内容がオブジェクトとして格納されます。
また、画面上の単純なDOM要素だけではなく、リスト表示をしている
<ul>タグ内の<li>タグ
に対して、
リストの順をドラッグ&ドロップで並び変える。等の動作も可能になります。