Ditto と jQuery で Ajax メニューを作る

最終更新日
2017.10.15

移行作業中のため一部のページが正しく表示されていない場合があります(o-ω-))

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

MODX は Ajax との相性が非常に良く、JavaScript だけ書けば、PHP を自分で書かなくても標準のリソースやスニペットで色々なものを自由に作成する事ができます。Ditto には JSON を表示する機能があるので簡単な GET 用の API ならこれを利用して作る事が出来ます。 JSON を使用する例として簡単な Ajax メニューを作り方を紹介します。

デモ

タブメニューのサンプル

JSON 用のリソースを Ditto で作成

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

クエリーが付くのでカテゴリ毎にキャッシュされます。

[[Ditto?
  &extenders = `request`     // リクエスト取得の拡張機能を使用
  &good = `parents`          // parentsパラメータのみ許可する
  &format = `json`           // JSON形式で表示
  &orderBy = `editedon DESC` // 表示順
  &display = `20`            // 最大取得数
]]

追加パラメータの &good で parents のみリクエストを許可します。記事の表示順や取得数などはここで設定しておきます。

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

request 拡張の追加パラメータ

request を有効にすると以下のパラメータが使用できるようになります。

good 許可するパラメータを指定する。
goodがある場合はそれのみ許可されるので付けるの推奨。
bad 許可しないパラメータを指定する
stripTags パラメータで送られた値からタグを削除する

メニューの作り方

これはおまけですが概要は親メニューを Wayfinder で表示して、内容を Ditto で表示させるようにします。
親メニュー → 小メニューというよりは、親メニュー → 記事一覧の表示向けですね。
親メニューを動的に吐かなくてもいいなら(アクティブメニューか判断しない)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>

JavaScriptの記述

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

(function ($) {
  $(function () {
    var eWrapperName = '.demo1'
    var eWrapper = $(eWrapperName)
    var eBlocks = eWrapper.children('.blocks')
    var eTabs = $('<div class="tabs"></div>')
    eWrapper.prepend(eTabs)
    eBlocks.append($('<div class="log"><span class="loading">Loading...</span></div>').hide())
    eBlocks.find('.tab').prependTo(eTabs)
    eBlocks.find('.block').hide()
    eWrapper = eBlocks = eTabs = null

    $(eWrapperName).on('click', '.tab', function (e) {
      e.preventDefault()
      console.log('click')
      $(this).siblings('.tab').removeClass('is_enabled')
      var eBlocks = $(eWrapperName).children('.blocks')

      var docid = $(this).attr('data-docid')
      var eCurrent = eBlocks.children('.block[data-docid="' + docid + '"]')
      var eLog = eBlocks.children('.log')

      if (!eCurrent.children('.doclist').length) {
        eLog.show()
        $.ajax({
          type: 'GET',
          url: 'modx/tips/ditto-ajaxmenu/menu.json',
          dataType: 'json',
          data: {
            ditto_parents: parseInt(docid)
          }
        }).done(function (response) {
          eBlocks.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>'
          }
          eCurrent.append('<ul class="doclist">' + html + '</ul>').fadeIn()
          eLog.fadeOut()
        }).fail(function () {
          eLog.replaceWith('<div class="log error">データが読み込めませんでした</div>')
        })
      } else {
        eBlocks.children('.block').hide()
        eCurrent.fadeIn()
      }
      $(this).addClass('is_enabled')
    })

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

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');