マウスドラッグ、マウスドロップ時の 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>タグ
に対して、
リストの順をドラッグ&ドロップで並び変える。等の動作も可能になります。