JavaScriptでドラッグ&ドロップ。


リスト要素をドラッグして順番を変えられます(サンプル)。
オレオレイベント予約関数だとremoveEventListenerできない
だめじゃん。removeEventListenerできないとstopDragできない。
//removeEventListenerが必要なければこれでいいんだが・・・
Event.observe = function(_el, _func, _callback) {
if (_el.addEventListener) {
_el.addEventListener(_func, _callback, false);
} else if (_el.attachEvent) {
_el.attachEvent("on"+_func, _callback);
}
}
で、シコシコif
文で分岐させました。もっかいJavaScript第5版読んでリスナー解除できるイベントユーティリティ書こう。
ドラッグ&ドロップはイベントたらい回しまくりです。それぞれのli要素にmouseDownイベントをリスナー登録しておきます。mouseDownイベントが送信されたらstartDrag関数を呼びwindowオブジェクトにmouseMoveイベントをリスナー登録、同時にwindowオブジェクトにmouseUpイベントをリスナー登録。mouseUpイベントが送信されたらstopDrag関数を呼びmouseMoveイベントとmouseUpイベントをリスナー解除します。
こいつらをリスナー解除させるためにイベントハンドラは全部ローカル変数に格納。スコープを通したいので変数もほとんどローカル変数。外から見る必要がある変数だけthisキーワードで宣言しています。JavaScriptではなるべくローカル変数使った方が取り回しがいいのかも。
このとき、なぜかIEではwindow.attachEventではなくdocument.attachEventでないと動きません。マウス関係のイベントだけそうなの?
ドロップの仕組み
晴れてmouseMoveイベントが送信できたら今度はドロップです。試行錯誤の結果、ダミー要素をマウスに追随させ、この要素と、もともとのリスト要素群の間に挿入されたドロップ候補とで接触判定をすることにしました。
接触していたらドロップ候補を表示します。mouseUpイベントが送信された時接触しているドロップ候補があれば、そのドロップ候補の直前にドラッグ対象になった要素をinsertBeforeして、ダミー要素やドロップ候補をすべて削除、ドラッグ&ドロップを終了します。

IE6だけロード直後にテキストノードしかクリックできない
li要素の中身がテキストノードだと、テキストの部分しかクリックできない。一旦、テキストをドラッグするとli要素全体がクリック可になる。unselectableとかいろいろやってみたが直らない。のおおおおおおおおおぉぉぉぉぉぉぉぉぉぉぉぉぉぉ!!!!!!!!!!!!
JavaScriptでもdispatchEvent
このEventDispatcherをそのまま使わせていただきました。ASのようにカスタムイベントを発射できる。すばらしい。