DD: Reordering a List
YUIのDrag and Dropリストを並べ替えるサンプル(サンプルページはこちら)。
ソースコードを見ると、いきなり(前のサンプルに比べて)えげつないコードが書かれていることがわかる。
Drag and Dropは、YAHOO.util.DDProxyを継承して作成されたオブジェクトで実装されている。
以下に、注釈つきのhtml全文を貼るが、一部、YUIのソースコードを追った部分もあった。
DragOverでリストを移動するところなどは巧妙に思うが、なにかもう少しどうにかなんないのかな、というロジックを含んでいたりする。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE>Ajax_Sampling</TITLE> <style type="text/css"> div.workarea { padding:10px; float:left } ul.draglist { position: relative; width: 200px; height:240px; background: #f7f7f7; border: 1px solid gray; list-style: none; margin:0; padding:0; } ul.draglist li { margin: 1px; cursor: move; zoom: 1; } ul.draglist_alt { position: relative; width: 200px; list-style: none; margin:0; padding:0; /* padding bottomは、targetableなリストが空だったときのため。 */ padding-bottom:20px; } ul.draglist_alt li { margin: 1px; cursor: move; } li.list1 { background-color: #D1E6EC; border:1px solid #7EA6B2; } li.list2 { background-color: #D8D4E2; border:1px solid #6B4C86; } /* #user_actions { float: right; } */ </style> <!-- 読み込むjs --> <script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js" > </script> <script type="text/javascript" src="scripts/yui/dragdrop/dragdrop-min.js" > </script> <script type="text/javascript" src="scripts/yui/animation/animation-min.js" > </script> <script type="text/javascript"> var Dom = YAHOO.util.Dom; var Event = YAHOO.util.Event; var DDM = YAHOO.util.DragDropMgr; ////////////////////////////////////////////////////////////////////////////// //「現在の順番」表示、「バックグラウンドの消去」の処理。 ////////////////////////////////////////////////////////////////////////////// YAHOO.example.DDApp = { init: function() { var rows=3,cols=2,i,j; for (i=1;i<cols+1;i=i+1) { // ul要素はddTargetとしてインスタンス化 new YAHOO.util.DDTarget("ul"+i); } for (i=1;i<cols+1;i=i+1) { for (j=1;j<rows+1;j=j+1) { // ul要素はddListとしてインスタンス化 new YAHOO.example.DDList("li" + i + "_" + j); } } Event.on("showButton", "click", this.showOrder); Event.on("switchButton", "click", this.switchStyles); }, //「現在の順番」を拾う処理 showOrder: function() { // この関数は下で使う。 var parseList = function(ul, title) { var items = ul.getElementsByTagName("li"); var out = title + ": "; for (i=0;i<items.length;i=i+1) { out += items[i].id + " "; } return out; }; var ul1=Dom.get("ul1"), ul2=Dom.get("ul2"); alert(parseList(ul1, "List 1") + "\n" + parseList(ul2, "List 2")); }, //「バックグラウンドの消去」の処理 switchStyles: function() { Dom.get("ul1").className = "draglist_alt"; Dom.get("ul2").className = "draglist_alt"; } }; ////////////////////////////////////////////////////////////////////////////// // custom drag and drop implementation ////////////////////////////////////////////////////////////////////////////// // DDProxyを継承して、DDListを作成するための準備。 YAHOO.example.DDList = function(id, sGroup, config) { // スーパークラスのコンストラクタの呼び出し。 YAHOO.example.DDList.superclass.constructor.call(this, id, sGroup, config); //this.logger = this.logger || YAHOO; var el = this.getDragEl(); // Domを1/3の透過とする。 Dom.setStyle(el, "opacity", 0.67); this.goingUp = false; this.lastY = 0; }; //DDProxyを継承して、DDListを作成する。 //thisは、DDListのインスタンスをあらわす。 YAHOO.extend(YAHOO.example.DDList, YAHOO.util.DDProxy, { // startDragのoverride startDrag: function(x, y) { // this.logger.log(this.id + " startDrag"); // ドラッグされるProxyElementを取得 var dragEl = this.getDragEl(); // ドラッグされる元のElementを取得 var clickEl = this.getEl(); // 元のエレメントは、非表示にする。 Dom.setStyle(clickEl, "visibility", "hidden"); // ProxyElementに、元のエレメントの要素を追加。 dragEl.innerHTML = clickEl.innerHTML; // ProxyElementに、元のエレメントのスタイルをコピー。 Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color")); Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor")); Dom.setStyle(dragEl, "border", "2px solid gray"); }, // endDragのoverride // startDragされて、Dropされずに、Mouseupされた場合に元に戻す処理。 endDrag: function(e) { // thisは、DDListのインスタンスをあらわす。 // DDListはli要素についてインスタンス化される。 // ドラッグさている元Elementを取得 var srcEl = this.getEl(); // ドラッグされているProxyElementを取得 var proxy = this.getDragEl(); // ProxyElementをみせて、元Elementの位置を取得して // Anim(Motion)で戻る。 Dom.setStyle(proxy, "visibility", ""); var a = new YAHOO.util.Motion( proxy, { points: { to: Dom.getXY(srcEl) } }, 0.2, YAHOO.util.Easing.easeOut ) var proxyid = proxy.id; // 元エレメントのID var thisid = this.id; // 上のMotionが終わったら、proxyを消して、元のエレメントを見せる。 a.onComplete.subscribe(function() { Dom.setStyle(proxyid, "visibility", "hidden"); Dom.setStyle(thisid, "visibility", ""); }); //Motionの発火 a.animate(); }, // onDragOverで挿入が行われていることに注意。 // (注)idは、Dragされた要素が、落っことされた要素のid onDragDrop: function(e, id) { // DDM.interactionInfo.dropはonDragDropでinteractions Pointを // 意味する。(APIドキュメント) // 1回のInteractionが行われたら。 // ただしい移動(挿入、移動)後はinteraction情報がクリアされる。 // 以下の条件を満たすのは、liがUL(のドロップゾーン)にinteractする場合。 // これ以外は、endDragで戻してしまう。 if (DDM.interactionInfo.drop.length === 1) { // Interact(Drop)したときのカーソルポジション // ※PointはYAHOO.util.Point(PointはRegionのサブクラス) var pt = DDM.interactionInfo.point; // Interact(Drop)したときの元要素の領域 // ※RegionはYAHOO.util.Regionのサブクラスと思われる。 var region = DDM.interactionInfo.sourceRegion; // regionがpointを含まなければ、子要素として追加。 if (!region.intersect(pt)) { //Dropされた場所の要素を取得。(ULを拾っている) var destEl = Dom.get(id); //Drag and Dropされたオブジェクト var destDD = DDM.getDDById(id); //this.getElで取れるのは、移動元のelement //移動元のエレメントをDropされたElementの最後の子要素にする。 destEl.appendChild(this.getEl()); destDD.isEmpty = false; //DDの座標のリセット(Interaction情報のクリア) DDM.refreshCache(); } } }, // Drag中の処理。 onDrag: function(e) { // y軸方向の移動量 var y = Event.getPageY(e); if (y < this.lastY) { // goingUpはプロパティーとして定義されていることに注意 this.goingUp = true; } else if (y > this.lastY) { this.goingUp = false; } // lastYもプロパティーとして定義されていることに注意 this.lastY = y; }, // (注)idは、DragOverされた要素のid // DragOverされたら、挿入してしまう。 onDragOver: function(e, id) { // ドラッグされた元要素のelementの取得 var srcEl = this.getEl(); // ドラッグオーバーされた要素のelementの取得 var destEl = Dom.get(id); // li要素にドラッグオーバーされたら、以下の処理。 if (destEl.nodeName.toLowerCase() == "li") { var orig_p = srcEl.parentNode; var p = destEl.parentNode; // このDDListのインスタンスが上を向いて動いていれば、前に挿入 // goingUpは、onDragで計ってある。 if (this.goingUp) { p.insertBefore(srcEl, destEl); // insert above // そうでなければ、後に挿入 } else { p.insertBefore(srcEl, destEl.nextSibling); // insert below } //DDの座標のリセット(Interaction情報のクリア) DDM.refreshCache(); } } }); Event.onDOMReady(YAHOO.example.DDApp.init, YAHOO.example.DDApp, true); </script> </HEAD> <BODY> <div class="workarea"> <h3>List 1</h3> <ul id="ul1" class="draglist"> <li class="list1" id="li1_1">list 1, item 1</li> <li class="list1" id="li1_2">list 1, item 2</li> <li class="list1" id="li1_3">list 1, item 3</li> </ul> </div> <div class="workarea"> <h3>List 2</h3> <ul id="ul2" class="draglist"> <li class="list2" id="li2_1">list 2, item 1</li> <li class="list2" id="li2_2">list 2, item 2</li> <li class="list2" id="li2_3">list 2, item 3</li> </ul> </div> <div id="user_actions"> <input type="button" id="showButton" value="現在の順番" /> <input type="button" id="switchButton" value="バックグラウンドの消去" /> </div> </BODY> </HTML>
画面は下のイメージ。