Wayfinder+Ditto+jQueryでAjaxメニューを作ってみる

このエントリーをはてなブックマークに追加

MODXはAjaxとの相性が非常に良いCMSで、特にPHPを組んだりしなくてもJavascriptを書けば標準のリソースやメジャーなスニペットで色々なものを自由に作成する事ができます。
DittoにJSONを表示する機能があるのでそれを使ってAjaxメニューを作ってみます。

デモ

demo

メニューの作り方

概要は親メニューをWayfinderで表示して、内容をDittoで表示します。
親メニュー→小メニューというよりは、親メニュー→記事一覧の表示向けですね。
親メニューを動的に吐かなくてもいいなら(アクティブメニューか判断しない)、Wayfinderは必要ないです。

メニューを表示させたい場所に親メニューを表示するためのWayfinderを記述します。

[[Wayfinder?
  &startId=`[ID]`
  &level=`1`
  &outerClass=`ajaxmenu` &rowTpl=`@CODE: <li data-docid="[+wf.docid+]"> <div class="tab"><a href="[+wf.link+]">[+wf.linktext+]</a></div> </li>` ]]

JSONを表示するためのリソースを作成します。Dittoの拡張機能のrequestを有効にして、URLパラメータを受け取れるようにします。
適当な場所に新規リソースを作成して内容を以下のように記述し、使用するテンプレートは(blank)にします。
とりあえずエイリアスは「menujson」にしました。メニューに表示と検索対象にするは外しておきましょう。

[[Ditto?
  &extenders=`request`
  &good=`parents`
  &format=`json`
  &orderBy=`editedon DESC`
  &display=10
]]

追加パラメータの&goodでparentsのみリクエストを許可します。記事の表示順や取得数などはここで設定しておきます。
中身がDittoなのでページネーションも実装できそうです。

requestの追加パラメータ

good 許可するパラメータを指定する。
goodがある場合は指定されていない物以外除外される。
bad 許可しないパラメータを指定する
stripTags タグを削除する

menujson.htmlにhttp://example.com/menujson.html?ditto_parents=1というような感じでアクセスしてJSONが表示されればOKです。

展開用のjavascriptは以下の様な感じです。

$(document).ready(function() {
  // 記事用のタグを(非表示にして)作成
  $(".ajaxmenu>li").append($("<ul class=\"docs\"></ul>").hide());
  // ローディング用のタグ(非表示にして)を作成
  $(".ajaxmenu>li").append($("<div class=\"log loading\">loading...</div>").hide());
  // 親メニューの.tabにイベントを登録
  $(".ajaxmenu>li>.tab").toggle(function(e) {
    e.preventDefault();
    // クリック-記事を表示する

    // アクティブな要素を取り出す
    var e_li = $(this).parent("li");
    var e_docs = $(this).parent("li").children(".docs");
    var e_log = $(this).parent("li").children(".log");

    // まだ記事が取得されていなくて、エラーがなければAjaxで記事を取得して表示
    if (!e_docs.children().size() && !e_li.children('.error').size()) {
      var res = $.ajax({
        type : "GET",
        url : "http://example.com/ditto_json.html",
        dataType : "json",
        data : {
          ditto_parents : parseInt($(this).parent("li").data("docid"))
        },
        beforeSend : function() {
          // ローディングを表示
          e_log.show();
        },
        success : function(response) {
          // 取得した記事をリストに追加
          if ($(response.entries).size()) {
            $(response.entries).each(function() {
              e_docs.append("<li><a href=\"" + this.link + "\">" + this.title + "</a></li>");
            });
          } else {
            // 記事が1個もない場合
            e_docs.append("<li>No documents found.</li>");
          }
          // フェードインで表示
          e_docs.fadeIn();
          // ローディングを隠す
          e_li.children(".log").hide();
        },
        error : function() {
          // 取得できなければログ
          e_li.children(".log").replaceWith("<div class=\"log error\">cannot load file.</div>").show();
        }
      });
      delete res;
    } else if (!e_li.children('.error').size()) {
      // エラーがなければ表示(2回目以降のクリック)
      e_docs.fadeIn();
    }
  }, function() {
    // クリック-記事を隠す
    $(this).parent("li").children(".docs").hide();
  });
});

勢いで書いたので無駄な処理をしていそうですが、サンプルのコードは自由にカスタマイズしてご利用ください。
IEのキャッシュ問題は考慮していませんが(メニューなのでブラウザ閉じて更新されれば特に問題ないかなぁと思ったので)必要なら、パラメータにユニークな数字などを付け加えてください。

アクティブなメニューの内容をあらかじめ表示させておく

トップページなどに1箇所だけに表示する場合は必要ないですが、サイドバー用のナビゲーションメニューとして使いたい場合アクティブなメニューは予め表示させておきたいところです。
予めアクティブなメニューや特定のメニューを表示したい場合は以下のように調整します。

方法1)ページロード時点でクリックしたことにする。

特定のメニューを表示させておきたい場合は$(document).readyの中の最後の行にtrigger('click')を追加しておけばすでにクリックされた状態になります。

$('.ajaxmenu>li.active>.tab').trigger('click');

この場合activeクラスに関係なくdata-docidを指定する事もできます。

方法2)Wayfinderのアクティブメニュー用テンプレートを作成する。

Wayfinderのアクティブな要素用のチャンクを作成します。
@CODEの入れ子はできないようなので、Wayfinderのチャンクを作成し中にDittoのコールを付け加えます。

[[Wayfinder?
  &startId=`[ID]`
  &level=`2`
&outerClass=`ajaxmenu`
&activeParentRowTpl=`wf.ajaxmenu.activeRow` &rowTpl=`@CODE: <li data-docid="[+wf.docid+]"> <div class="tab"><a href="[+wf.link+]">[+wf.linktext+]</a></div> </li>` ]]

levelは2にしないとactiveParentRowTplが使用できませんでした。

wf.ajaxmenu.activeRowチャンクの内容

<li data-docid="[+wf.docid+]">
  <div class="tab"><a href="[+wf.link+]">[+wf.linktext+]</a></div>
  <ul class="docs">
    [[Ditto?
      &parents=`[+wf.docid+]`
      &tpl=`@CODE:<li><a href="[~[+id+]~]">[+title+]</a></li>`
      &orderBy=`editedon DESC`
      &display=10
    ]]
  </ul>
</li>

アクティブなメニューはAjaxを使用しないで直接表示させます。
見栄え的にJSONのDittoと同じようなフォーマットにするといいですね。
最後にjQueryの最初の3,7行を以下のように書き換えてactiveクラスが付いた要素を除外するようにします。

	$(".ajaxmenu>li").not(".active").append($("<ul class=\"docs\"></ul>").hide());
	$(".ajaxmenu>li").not(".active").children(".tab").toggle(function() {

動作テストをするときはJSONのDittoはキャッシュを無効にしておくと分かりやすいです。

つづく(!?)

Edited on 2014.05.13 Created on 2012.09.24 jQueryMODXチュートリアルWebデザインEvo
PAGE TO TOP