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

MODX の Wayfinder+Ditto+jQuery で Ajax なメニューを作成します。

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

デモ

タブメニューのサンプル

メニューの作り方

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

Wayfinderを記述する

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

<div class="demo1">
[[Wayfinder?
  &startId=`[ID]`
  &level=`1`
  &rowClass=`block`
  &outerTpl=`@CODE:<div class="blocks">[+wf.wrapper+]</div>`
  &rowTpl=`@CODE:
  <div data-docid="[+wf.docid+]"[+wf.classes+]>
    <div class="tab" data-docid="[+wf.docid+]"><a href="[+wf.link+]">[+wf.linktext+]</a></div>
  </div>`
]]
</div>

Dittoのリソースを作成

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

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

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

requestの追加パラメータ

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

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

JavaScriptの記述

サンプルで使っているものですが、よかったら参考にしてください。

$(function() {

    var e_wrapper_name = '.demo1';
    var e_wrapper = $(e_wrapper_name);
    var e_blocks = e_wrapper.children('.blocks');
    var e_tabs = $('<div class="tabs"></div>');
    e_wrapper.prepend(e_tabs);
    e_blocks.append($('<div class="log"><span class="loading">Loading...</span></div>').hide());
    e_blocks.find('.tab').prependTo(e_tabs);
    e_blocks.find('.block').hide();
    e_wrapper = e_blocks = e_tabs = null;

    $(e_wrapper_name).on('click', '.tab', function(e) {
        e.preventDefault();
        $(this).siblings('.tab').removeClass('is_enabled');
        var e_blocks = $(e_wrapper_name).children('.blocks');

        var docid = $(this).attr('data-docid');
        var e_current = e_blocks.children('.block[data-docid="' + docid + '"]');
        var e_log = e_blocks.children('.log');

        if (!e_current.children('.doclist').length) {
            e_log.show();

            $.ajax({
                type: 'GET',
                url: 'modx/tips/ditto-ajaxmenu/menu.json',
                dataType: 'json',
                data: {
                    ditto_parents: parseInt(docid)
                }
            }).done(function(response) {
                e_blocks.children('.block').hide();
                var html = '';
                if ($(response.entries).length) {
                    $(response.entries).each(function() {
                        html+='<li><a href="' + this.link + '">' + this.title + '</a></li>';
                    });
                } else {
                    html+='<li>記事はありません</li>';
                }
                e_current.append('<ul class="doclist">'+html+'</ul>').fadeIn();
                e_log.fadeOut();
            }).fail(function() {
                e_log.replaceWith('<div class="log error">データが読み込めませんでした</div>');
            });
        }
        else {
            e_blocks.children('.block').hide();
            e_current.fadeIn();
        }
        $(this).addClass('is_enabled');
    });

    $(e_wrapper_name + ' .tab[data-docid="43"]').trigger('click');
});

CSSの記述

こちらもサンプルで使用しているスタイルです。

@charset "UTF-8";
.demo1 {
    position: relative;
}

.demo1 .tab {
    display: table-cell;
    background: #e4e4e4;
    padding: 8px 10px;
    border-top: 1px solid #ccc;
    border-right: 1px solid #ccc;
    cursor: pointer;
}

.demo1 .tab a {
    text-decoration: none;
}

.demo1 .tab.is_enabled {
    background: #fff;
}

.demo1 .tab:first-child {
    border-left: 1px solid #ccc;
    border-radius: 3px 0 0 0;
}

.demo1 .tab:last-child {
    border-radius: 0 3px 0 0;
}

.demo1 .blocks {
    margin: 0;
    padding: 0;
    border: 1px solid #ccc;
    position: relative;
    min-height: 50px;
}

.demo1 .log {
    position: absolute;
    top: 0;
    width: calc(100% - 20px);
    height: calc(100% - 20px);
    background: rgba(220, 220, 220, .9);
    padding: 10px;
    text-align: center;
}

.demo1 .loading {
    position: absolute;
    display: block;
    top: 50%;
    width: 100%;
    height: 1em;
    margin-top: -0.5em;
}

.demo1 {
    width: 640;
    position: relative;
}

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

予め特定のメニューを表示しておく場合は以下のように調整します。

最後の行にtrigger('click')を追加しておけばすでにクリックされた状態になります。

$('.demo1 .tab[data-docid="43"]').trigger('click');
Edited on 2017.02.11 Created on 2012.09.24 MODX チュートリアル Webデザイン Evo JavaScript