X-0017 onDragOver/onDragExit 時にドラッグしているデータそのものを得る

前口上

nsDragAndDrop を使ったオブジェクトのドラッグ&ドロップ実装では、 onDragStart, onDragOver, onDragExit, onDrop などのメソッドを持った要素をオブザーバーとして登録するだけで、ドラッグ&ドロップで発生する各種イベントに対して適切な動作を割り当てることができます。

予め用意されたラッパーの不満点

しかし、僕はこの nsDragAndDrop に二つの不満を持っています。一つは、 onDrop 以外のメソッドではドラッグしているオブジェクトのデータを引数として受け取れないこと。もう一つは、 onDragExit ではオブザーバーの受け入れ可能なデータの ContentType のチェックが行われないことです。

例えばSidebarウィンドウでは、サイドバーをドラッグした時にフローティング Sidebar を自動で表示するという機能を実装しています。これは普通に実装すれば、 onDragExit で受け入れ可能な ContentType のチェックが行われないためにリンクなどの無関係なオブジェクトが要素の上を通過しただけでもフローティング Sidebar が起動してしまうし、 onDragExit メソッドに渡される引数からオブジェクトの ContentType を調べようにも onDragExit には TransferData も Flavour も渡されないので ContentType が調べられない、という事になってしまいます。 onDragExit と onDragOver などを組み合わせれば回避できるかもしれませんが、僕は無精なので、 onDragExit だけで全部処理したいと思ってしまいます。

問題の回避策

こんなヘンな使い方にどれほどの意味があるのかはさておき、そういう使い方をすることに決めてしまった以上は、どうにかしてドラッグ中のデータの ContentType を調べなければなりません。

nsDragAndDrop の内部で使われている方法を使うと、 onDragOver でも onDragExit でも関係無しに、ドラッグ中のデータを取り出すことができます。やり方は以下の通りです。

onDragExit : function(aEvent, aSession) {
    var XferDataSet = nsTransferable.get(
            this.getSupportedFlavours(),
            nsDragAndDrop.getDragData,
            true
        );
        var XferData     = XferDataSet.first.first;
        var doraggedData = XferData.data;
        var contentType  = XferData.flavour.contentType;
/* 
        // 複数のオブジェクトをドラッグ中の場合、
        // 以下のようにすれば各データについて調べられる。
        var XferData, doraggedData, contentType;
        for (var i = 0; i < XferDataSet.dataList.length; i++) {
            XferData     = XferDataSet.dataList[i].first;
            doraggedData = XferData.data;
            contentType  = XferData.flavour.contentType;
        }
 */
}

Sidebarウィンドウでは実際に、この方法を使っています。

余談

ちなみに、オブザーバーとして登録できるオブジェクトは、オブザーバー用に定義したものだけではありません。 XUL 要素でも、 onDragStart などのメソッドを持たせておけば、オブザーバーにできます。 this.mDraggedElement = event.target; 、のような形でドラッグ中のオブジェクトを別ルートで保持できたりするので、応用の幅が広がります。