jQueryではじめるAjax

第5回 jQuery UIによるユーザインターフェースの改良

この記事を読むのに必要な時間:およそ 15 分

お気に入りの実装(2)-Draggables / Droppables / Sortables

お気に入りをドロップ可能にする

ドラッグしたサムネイルをお気に入りドロップ可能にします。droppableメソッドを呼び出すだけでドラッグが可能になります。とても簡単ですね。 droppableメソッドには様々なオプションが用意されています。

表5 droppableメソッドのオプション(抜粋)

プロパティ説明デフォルト値
accept ドロップを受け入れる要素を指定する。jQueryセレクタで記述する なし
activeClass ドロップ可能な要素がドラッグを開始したときに適用するクラス名を指定する なし
hoverClass ドロップ可能な要素が通過したときに適用するクラス名を指定する なし
drop 要素がドロップされたときに呼び出されるコールバック関数を指定する なし

他のオプションについては,UI/Droppables/droppable - jQuery JavaScript Libraryをご覧ください。

ドロップを可能にする実装は次のようになります。
$(function(){・})の初期処理に追加しますリスト14⁠。

リスト14 ドロップを可能にする

$("#favorites > ul")
    // お気に入りを並び替え可能にする
    .append("<li/>").sortable({axis: "y"}).empty()
    // ドロップ可能にする
    .droppable({
        activeClass: "droppable-active",    // draggableアイテムがアクティブになったときのクラス
        hoverClass: "dropped-hover",        // draggableアイテムが通過するときのクラス
        accept: ".thumbnail",               // thumbnailクラスの要素をドロップ可能にする
        drop: function(ev, ui) {            // ドロップしたときの処理
            // 同じお気に入りがあるかどうか
            var exists = $.grep($("#favorites li"), function(item, index){
                return $(item).children("img").attr("src") == $(ui.draggable).children("img").attr("src");
            });

            // 新しいお気に入り
            if (exists.length == 0) {
                // 要素を複製
                var item = $(ui.draggable).clone();
                // 追加するli要素を作成
                $("<li/>")
                    .append(item.children("img")).append("<br/>")
                    .append(item.children(".title").clone())
                    .append($("<a/>").addClass("del").append("[x]").click(function(){$(this).parent().remove()}))
                    // クリックしたらプレーヤーを表示
                    .click(function(){
                        if (item.children(".video_url").text() == '') {
                            // プレーヤーページ(Youtube)
                            window.open(item.children(".player_url").text(), null)
                        } else {
                            // プレーヤー(ダイアログ)
                            yt.showPlayer({
                                url  : item.children(".video_url").text(),
                                title: item.children(".title").text(),
                                info : item.children(".video_info").clone()
                            });
                        }
                    })
                    .prependTo(this);   // 最上位に追加
            } else {
                $(exists)
                    .prependTo(this);   // 最上位に移動
            }

            $(this).sortable("refresh");    // 並び替え項目に変更があったことを知らせる
        }
    });

まず,activeClassプロパティに,"droppable-active",hoverClassに"dropped-hover"を指定しています。droppable-activeクラスとdropped-hoverクラスはcssで次のように定義しましたリスト15⁠。

リスト15 droppable-active / dropped-hoverの定義

.droppable-active {
    background-color: #b0c4de;
}

.dropped-hover {
    background-color: #6495ed;
}

ドラッグ中にドラッグ先が分かるように,また,ドロップ要素の上を通過していることが一目で分かるように背景色を変えています。

図6 ドラッグ中(activeClass)

図6 ドラッグ中(activeClass)

図7 ドロップ要素の上を通過中(hoverClass)

図7 ドロップ要素の上を通過中(hoverClass)

cssの定義の順番に注意

activeClass / hoverClass クラスのcssの定義は,activeClass,hoverClassの順に定義してください。 これは,jQueryが内部的に次のようにクラスを設定していることによります。

ドラッグ中
<droppable要素 class="ui-droppable activeClassで指定したクラス">
ドロップ要素の上を通過中
<droppable要素 class="ui-droppable activeClassで指定したクラス hoverClassで指定したクラス">

ドロップ要素の上を通過中にも,ドラッグ中(activeClass)で指定したクラスが残っている事に注目してください。

もし,cssでhoverClass,activeClassの順番で定義されていた場合,重複している項目については,hoverClassの内容をactiveClassの内容で上書きしてしまいます。 たとえばこの場合は,hoverClassで指定された背景色は適用されません。

acceptプロパティには, ドロップを受け入れる要素として,thumbnailクラスの要素を指定しています。

dropプロパティには, ドロップされたときに実行されるコールバック関数を指定しています。

drop: function(ev, ui) {            // ドロップしたときの処理
    ・・・
});

このコールバック関数は2つの引数が渡されます。

1つめの引数は,イベントオブジェクトです。
2つめの引数は,ドロップされたdraggable要素に関するオブジェクトです。たとえば,この引数をuiと定義した場合,ui.instanceとすることでドロップされた要素を取得することができます。

その他の詳細については,UI/Droppables/droppable - jQuery JavaScript Libraryをご覧ください。

まず,アイテムの重複を避けるため,既にお気に入りに同じアイテムが存在しているかどうかをチェックします。 ここでは,同じURLのimg要素を持っているかどうかで同じアイテムかどうかを判別しています。

// 同じお気に入りがあるかどうか
var exists = $.grep($("#favorites li"), function(item, index){
    return $(item).children("img").attr("src") == $(ui.draggable).children("img").attr("src");
});

この変数existsには,お気に入りに重複したアイテムが存在した場合,そのli要素を含む配列が返されます。もし,存在しない場合は,空の配列が返されます。よって,重複があるかどうかを判別するには次のように記述します。

if (exists.length == 0) {    // 存在しない

} else {    // 存在する

}
重複したお気に入りが存在しない場合

新しいお気に入りアイテムを作成します。

まず,ドロップされた要素を複製します(要素を複製しないでそのまま追加すると,移動してしまいます⁠⁠。

// 要素を複製
var item = $(ui.draggable).clone();

次に,リストのli要素を作成します。

// 追加するli要素を作成
$("<li/>")

サムネイル画像,タイトル,削除ボタンを追加します。

.append(item.children("img")).append("<br/>")
.append(item.children(".title").clone())
.append($("<a/>").addClass("del").append("[x]").click(function(){$(this).parent().remove()}))

クリック時のイベントハンドラを追加します。この処理は,サムネイルのクリックイベントハンドラど同じです。 サムネイルの要素から,ビデオのurl,タイトル,ビデオの詳細情報をコピーして,プレーヤーを起動します。

// クリックしたらプレーヤーを表示
.click(function(){
    if (item.children(".video_url").text() == '') {
        // プレーヤーページ(Youtube)
        window.open(item.children(".player_url").text(), null)
    } else {
        // プレーヤー(ダイアログ)
        yt.showPlayer({
            url  : item.children(".video_url").text(),
            title: item.children(".title").text(),
            info : item.children(".video_info").clone()
        });
    }
})

そして,作成したli要素を最上位に追加します。

.prependTo(this);   // 最上位に追加
重複したお気に入りが存在する場合

見つかったアイテムを最上位に移動します。

$(exists)
    .prependTo(this);   // 最上位に移動
並び替え可能な要素の変更を通知

最後に,並び替え可能な要素の変更を通知します。これを忘れると,マウスで並び替えることができない要素ができてしまいます。

$(this).sortable("refresh");    // 並び替え項目に変更があったことを知らせる

以上で全ての実装が終了しました。サンプルを実行して動作を確認してみてください。

また,今回のサンプルアプリケーションのソースコード一式をアーカイブしました。ダウンロードして動作と実装を確認してみてください。

著者プロフィール

池田正一(いけだまさかず)

仕事ではもっぱらJavaを使い,たまにC/C++を書かされ,WebサービスをRubyで開発するプログラマ。ドラえもん好きでドラえもんSuperDatabaseの管理人。stacktrace.jpにて頭の中のStackを出力中。