マウスドラッグ、マウスドロップについて
前回の投稿では、マウスでDOM要素をドラッグすることを試してみましたが、
要素をドラッグしたあと、ドロップする方法も試してみます。
ドラッグ&ドロップに関するイベントの種類は次のものがあります。
drag dragstart dragend dragenter dragleave dragexit dragover drop
それぞれ、イベントハンドラの意味は下記のようになります。
要素をドラッグしている時 drag ドラッグ開始したとき dragstart ドラッグが終了した時 dragend ドラッグ中の要素がドロップできる場所の上に入ったとき dragenter ドラッグしている要素がドロップできる場所から離れたとき dragleave 上記の「dragleave」と同義 dragexit ドラッグ中の要素がドロップできる場所の上にあるとき dragover ドラッグしている要素をドロップした時 drop
上記のイベントハンドラの意味を踏まえ、シンプルなドラッグ&ドロップのサンプルを試してみます。
下記の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: #9ed7ff;
}
#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.style.backgroundColor = "#d3edff";
});
// ドラッグ開始したとき
element_drag_dom.addEventListener('dragstart', function(e){
display_status('DRAG DOM : dragstart', 'drag_c');
});
// ドラッグが終了した時
element_drag_dom.addEventListener('dragend', function(e){
display_status('DRAG DOM : dragend', 'drag_c');
// ドラッグが終わったら、文字列、cssを戻す
element_drag_dom.innerHTML = "ドラッグ用DOM";
element_drag_dom.style.backgroundColor = "#9ed7ff";
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);
});
/**
* 処理ステータスを画面出力する
*/
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);
// ステータスを30個づつの表示用配列へ格納する
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に対して、ドラッグ&ドロップをする際の
それぞれのイベントが見えるように、
画面上に「イベント発生時の日時」と「イベントハンドラの状態(ステータス)」を出力してみました。
上記の出力は、サンプルの動作がわかりやすいように出力してます。
実際にはこのような出力はせず、必要な処理のみを書くように注意します。
余計な画面表示を行わず、シンプルな動作例を確認する場合は、
ソース上の「display_status」を全て削除し、下記のメソッドごと削除しても動作します。
/**
* 処理ステータスを画面出力する
*/
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);
// ステータスを30個づつの表示用配列へ格納する
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 />";
}
}
画面にアクセスするとわかるように、まず最初に、以下のような
ドラッグ用のDOMと、ドロップ領域用のDOMが画面に表示されます。

この状態から、ドラッグ用のDOM(画面上の青い領域)をマウスでドラッグ開始します。

そうすると、ドラッグ中の状態として、下記のステータスが画面に出力されます。
(実際にはマウスの挙動に応じて、出力される内容が変わりますので、色々と操作して確かめてみていただければと思います)
要素をドラッグしている時 ドラッグ開始したとき ドラッグが終了した時 ドラッグ中の要素がドロップできる場所の上に入ったとき ドラッグしている要素がドロップできる場所から離れたとき ドラッグ中の要素がドロップできる場所の上にあるとき ドラッグしている要素をドロップした時

ここでプログラム上の注意が必要な点としては、
各イベントハンドラの登録は「ドラッグ用のDOMに対して登録するイベント」と「ドロップ領域用のDOMに対して登録するイベント」がそれぞれ異なっている点です。
「ドラッグ用のDOMに対して登録するイベント」に対しては
drag dragstart dragend
であることに対し、
「ドロップ領域用のDOMに対して登録するイベント」に対しては
dragenter dragexit(実際には未登録) dragleave dragover drop
としている点です。
ドラッグ元のDOMに対して、ドロップ領域用のイベントハンドラを登録しても意図しない動作になるので、
「何をドラッグ」して、「どこにドロップ」するのか、という点に注意が必要になります。