はじめに
この連載では、XMLとXSLを使った高度なUIデザインに挑戦します。1回に1つずつ、XMLとXSL(XML用のスタイルシート言語)を使用して高度なユーザーインタフェース(UI)コンポーネントを作成していきます。
今回は、第1回に作成したフォルダツリーを発展させ、フォルダツリー内のエンティティをドラッグして別のツリー内にドロップする方法を紹介します。
ドラッグ&ドロップコントロール
今回のドラッグ&ドロップコントロール(DragControl
)はクライアント側に存在し、次のように定義されます。
function DragControl() { this.entity = null; this.target = null; this.origin = null; this.enabled = false; this.beginX = null; this.beginY = null; this.beginDrag = beginDrag; this.endDrag = endDrag; this.setTarget = setTarget; this.setPosition = setPosition; this.move = move; this.reset = reset; }
このコントロールには、次のメンバが含まれます。
プロパティ
entity
entity
プロパティには、ある場所から別の場所へと移動するオブジェクトへの参照が格納されます。移動は、同じツリー内での移動、または別のツリーへの移動のどちらでも構いません。このプロパティはbeginDrag()
メソッド内で設定されます。
target
target
プロパティには、現在のドラッグ&ドロップ操作のドロップ先への参照が格納されます。ユーザーがマウスポインタを別のエンティティに動かすと、値が変更されます。このプロパティはsetTarget()
メソッド内で設定されます。
origin
origin
プロパティには、オブジェクトのドラッグ元フォルダへの参照が格納されます。ユーザーがドラッグ先を指定せずにマウスの左ボタンを放した場合は、このフォルダの場所にエンティティが戻されます。origin
プロパティは、beginDrag()
メソッドによって設定されます。
enabled
ドラッグ&ドロップ操作を実際に開始するのはenabled
プロパティです。ユーザーがエンティティを選択した状態でマウスを一定ピクセル数だけドラッグすると、このプロパティがtrueに設定されます。今回のコードでは、開始とみなされるのに必要なドラッグ範囲が5ピクセルになっています。このプロパティの変更はmove()
メソッド内で行います。
beginX
このプロパティには、ドラッグ&ドロップルーチン開始時点のマウスポインタのX座標が入ります。beginX
プロパティはbeginDrag()
メソッド内で設定されます。
beginY
このプロパティには、ドラッグ&ドロップルーチン開始時点のマウスポインタのY座標が入ります。beginY
プロパティはbeginDrag()
メソッド内で設定されます。
メソッド
beginDrag()
beginDrag()
メソッドの定義は次のとおりです。
function beginDrag(obj) { if(window.event.button == 1) { document.onmousemove = function anonymous() { dragControl.move(obj) }; this.origin = obj.parentNode; this.entity = obj; this.beginX = window.event.x; this.beginY = window.event.y; window.event.cancelBubble = true; } }
このメソッドは、XSL変換(XSLT)スタイルシート内でエンティティに関連付けられています。
このメソッドでは、まず、(コンテキストメニュー用に使われる)マウスの右ボタンではなく、左ボタンによってイベントが発生したことを確認します。次に、適切なプロパティ値を設定し、クライアントブラウザ内でのイベント伝播をキャンセルします。
endDrag()
endDrag()
メソッドの定義は次のとおりです。
function endDrag() { if(this.entity != null) { this.entity.style.position = "static"; this.entity.style.left = null; this.entity.style.top = null; this.entity = this.entity.removeNode(true); if(this.target != null) { dragControl.target.appendChild(this.entity); if(this.target.open != "true") { clickOnEntity(this.target); } } else { this.origin.appendChild(this.entity); } document.onmousemove = null; document.onmouseup = null; this.entity = null; this.target = null; this.enabled = false; } }
このメソッドは、まずエンティティが選択されていることを確認します。次に、ユーザーが現在ドラッグ先を選択しているかどうかに応じて、エンティティを元の場所に戻すか(ドラッグ先が選択されていない場合)、新しい場所に移動させるか(ドラッグ先が選択されている場合)のいずれかを行います。
このメソッドは、XSLTスタイルシート内でエンティティに関連付けられています。
setTarget()
setTarget()
メソッドの定義は次のとおりです。
function setTarget(obj) { if(this.entity != null && this.entity != obj) { this.target = obj; } window.event.cancelBubble = true; }
このメソッドは、まず現在のエンティティが存在すること、およびこの選択中のエンティティが移動先のエンティティでないことを確認します。次に、DragControl
のtarget
プロパティを設定します。その後、DragControl
はクライアントブラウザ内でのイベント伝播をキャンセルします。
このメソッドは、XSLTスタイルシート内でエンティティに関連付けられています。
setPosition()
setPosition()
メソッドの定義は次のとおりです。
function setPosition() { this.entity.style.left = window.event.x; this.entity.style.top = window.event.y - 10; }
このメソッドは、選択されているエンティティのXおよびY座標を、単純に現在のマウスイベントのXおよびY座標に書き換えます。このメソッドはmove()
メソッド内から呼び出されます。
このメソッドは、サンプルの「tree.js」ファイル内でエンティティに関連付けられています。
move()
move()
メソッドの定義は次のとおりです。
function move(obj) { if(window.event.x < this.beginX - 5 || window.event.x > this.beginX + 5 || window.event.y < this.beginY -5 || window.event.y > this.beginY + 5 && this.enabled == false) { obj.style.position = "absolute"; obj.style.filter = "alpha(opacity='60')"; this.setPosition(); this.enabled = true; obj = obj.removeNode(true); document.body.appendChild(obj); document.onmouseup = function anonymous() { dragControl.endDrag() }; } else if(this.enabled == true) { this.setPosition(); } }
このメソッドは、まず、ユーザーが実際にエンティティを(X軸/Y軸、正/負を問わず)いずれかの方向に少なくとも5ピクセル以上ドラッグしていることを確認します。次に、このエンティティを現在のツリーから取り出してドキュメント本体に移動させます。エンティティが常にマウスカーソルの隣に表示されるようにするために、このメソッドをドキュメントオブジェクトのonmousemove
イベントを介して継続的に呼び出す必要があります。
このメソッドは、サンプルの「tree.js」ファイル内でエンティティに関連付けられています。
reset()
reset()
メソッドの定義は次のとおりです。
function reset() { document.onmouseup = null; document.onmousemove = null; this.entity = null; this.origin = null; this.target = null; }
このメソッドは、ドキュメントおよびDragControl
オブジェクトのクリーンアップだけを行います。ドラッグ&ドロップが完了またはキャンセルされると、必ずこのメソッドが呼び出されます。
このメソッドは、XSLTスタイルシート内でエンティティに関連付けられています。
それでは、フォルダツリーのドラッグ&ドロップコントロールのライブデモを見てみましょう。このデモには2つのツリーがあり、その下に、順序を示す「One」から「Six」までのタイトルを持つエンティティが用意されています。「One」の下に「Two」、「Two」の下に「Three」というようにそれぞれがネストされるようにドラッグしてみてください。図7に示すように、「One」というエンティティをTree1からTree2にドラッグすると、先ほど番号順に並べた(ネストさせた)「One」以下のすべてのエンティティもそれにならってTree1からTree2に移動します。
終わりに
本稿の内容が、Webアプリケーションインタフェースの質的向上に役立てば幸いです。質問・コメント・提案があれば、筆者紹介にあるアドレスまで遠慮なくメールをお送りください。