前回作成した物を利用してhtml5でドラッグアンドドロップの機能を追加してみたいと思います。
動作確認したところ、Firefoxでのみ作動しました。
ドラッグオーバーしている行にドロップするとその行の下に配置するようにしました。
同じテーブル内でも同様です。
古典的な手法かもしれませんが、ドラッグオーバーしている時、行の行数を調べるようにしています。
他の人はどのようにやっているのか。もっと良い方法を知りたいです。
前回のソースを改良、追加します。
行を移動させる際、追加するテーブルに行を作成し、値を全部コピー後、元の行を削除します。
コピーする際、イベントハンドラ、idもコピーすることを忘れないように。
イベントの詳細。
dragstart:ドラッグが開始される時に発生するイベント
dragenter:ドラッグしたものがドロップゾーンに入ったに発生するイベント
dragover:ドラッグしたものがドロップゾーン内に存在する時に発生するイベント
dragleave:ドラッグしたものがドロップゾーンから出た時に発生するイベント
drop:ドロップした時に発生するイベント
追加するソース。events.js
var com; if(!com) {com={};}; if(!com.sample) {com.sample={};}; if(!com.sample.Class) {com.sample.Eventclass={};}; var ev; if(!ev){ev=com.sample.Eventclass;}; function dragStartHandler(event) { ev.dragStartHandler(event, "td", "tr", "trid"); } function dropHandler(event) { ev.dropHandler(event, "trid"); } function dragOverHandler(event) { ev.dragOverHandler(event); } function dragOverHandlerForColn(event) { ev.dragOverHandlerForColn(event); } function dragEnterHandler(event) { ev.dragEnterHandler(event, "trid"); } ev.dragStartHandler = function(event, targetTagName, pTagName, key) { var trtop=al.findParent(event.target, pTagName); if(!al.searchRemainTr(trtop)) { event.preventDefault(); return; } if(event.target.tagName.toLowerCase() == targetTagName.toLowerCase()) { var tr = al.findParent(event.target, pTagName.toLowerCase()); event.dataTransfer.setData(key, tr.id); }else { event.preventDefault(); } } ev.dropHandler = function(event, key) { var id = event.dataTransfer.getData(key); var tagEle = document.getElementById(id); if(tagEle) { var t=event.currentTarget.insertRow(this.colnN.getColnNum()+1); t.id =id; t.ondragover=tagEle.ondragover; var len=tagEle.cells.length; for(var i=0;i<len;i++) { var tdtop=tagEle.cells[i]; var clonechild=tdtop.cloneNode(true); t.appendChild(clonechild); } tagEle.parentNode.removeChild(tagEle); } event.stopPropagation(); } ev.dragOverHandler = function(event) { event.preventDefault(); } ev.colnN={}; ev.dragoverColn = (function() { var colnNum = 0; var getColnNum = function() { return colnNum; }; var setColnNum = function(num) { colnNum = num; }; ev.colnN.getColnNum = getColnNum; ev.colnN.setColnNum = setColnNum; })(); ev.dragOverHandlerForColn = function(event) { var tr = al.findParent(event.target,"tr"); this.colnN.setColnNum(tr.sectionRowIndex); event.preventDefault(); } ev.dragEnterHandler = function(event, key) { var types = event.dataTransfer.types; for(var i =0;types.length;i++) { if(types[i] == key) { event.preventDefault(); return; } } }
修正するソース。
al.addTableRow=function(target) { var trtop=this.findParent(target, "tr"); if(trtop.tagName.toLowerCase() != "tr") { alert("親取得に失敗しました"); return; } var trtopParent=trtop.parentNode; <!--対象行のインデックスを取得し、+1--> var newColN=trtop.sectionRowIndex+1; var tr=trtopParent.insertRow(newColN); tr.id = this.createSuffix(); tr.ondragover=trtop.ondragover; var len=trtop.cells.length; for(var i=0;i<len;i++) { var tdtop=trtop.cells[i]; var clonechild=this.copyChild(tdtop); tr.appendChild(clonechild); } }
htmlの修正箇所。html5形式にします。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link href="tests.js" rel="Javascript" type="text/javascript" media="all" /> <link href="eventss.js" rel="Javascript" type="text/javascript" media="all" /> <script src="tests.js" type="text/javascript"></script> <script src="eventss.js" type="text/javascript"></script> <title>samples</title> </head> <body> <form id="form1"> <table border="1" id="tdata"> <thead></thead> <tbody id="tbody1" ondragstart="dragStartHandler(event)" ondragenter="dragEnterHandler(event)" ondragover="dragOverHandler(event)" ondrop="dropHandler(event)"> <tr id="ssddd" ondragover="dragOverHandlerForColn(event)"> <td draggable="true" > <div> <input type="text" id="form1sendt1" name="form1:sendt1" value="xx" size="30"/> <input type="button" value=" add " onClick="addTableRow(this);"/> <input type="button" value=" rem " onClick="remTableRow(this);"/> </div> </td> </tr> <tr id="ssddd2" ondragover="dragOverHandlerForColn(event)"> <td draggable="true" > <div> <input type="text" id="form1sendt2" name="form1:sendt2" value="" size="30"/> <input type="button" value=" add " onClick="addTableRow(this);"/> <input type="button" value=" rem " onClick="remTableRow(this);"/> </div> </td> </tr> </tbody> <tfoot></tfoot> </table> <br> <br> <table border="1" id="tdata2"> <thead></thead> <tbody id="tbody2" ondragstart="dragStartHandler(event)" ondragenter="dragEnterHandler(event)" ondragover="dragOverHandler(event)" ondrop="dropHandler(event)"> <tr id="ssddd23" ondragover="dragOverHandlerForColn(event)"> <td draggable="true" > <div> <input type="text" id="form1sendt21" name="form1:sendt21" value="xx" size="30"/> <input type="button" value=" add " onClick="addTableRow(this);"/> <input type="button" value=" rem " onClick="remTableRow(this);"/> </div> </td> </tr> <tr id="ss21d" ondragover="dragOverHandlerForColn(event)"> <td draggable="true"> <div> <input type="text" id="form1sendt22" name="form1:sendt22" value="" size="30"/> <input type="button" value=" add " onClick="addTableRow(this);"/> <input type="button" value=" rem " onClick="remTableRow(this);"/> </div> </td> </tr> </tbody> <tfoot></tfoot> </table> </form> </body> </html>
イベントハンドラはテーブルに付けた方が楽なのでテーブルに。
列にはマウスが乗っている時に列番を知るためにイベントハンドラを付与しています。
セルにはクリックOKの属性を付与しています。何故列にしないのかというと、ツリーを想像すると分かりますが、セルが一番上にあるため、クリック出来ないというのが理由です。。
なので、セルでイベントを捕捉後、そのセルの親である列のidを探し、datatransferへセットしています。
あとはドロップハンドラ内で列をコピーして、元のテーブルから削除しています。
動作確認した図です。
fig.1 基本テーブル
fig.2 上の表から持ってきた行を
fig.3 下の表に入れる
fig.4 下の表から持ってきた行を
fig.5 上の表に入れる
一応、希望する動きになりました。
ちなみにドラッグ中に異なる画像を表示させたい場合、datatransferオブジェクトに下記のような
感じで入れてやると画面が表示されます。
dragstartハンドラ内でdatatransferへsetDataしているところとか。
var dragIcon = document.createElement('img'); dragIcon.src = "./sa.png"; event.dataTransfer.setDragImage(dragIcon, -10, -10);