DittoとAjaxで「もっと読む」を簡単に実装する

Ditto を使って Ajax で「もっと読む」「さらに見る」のリンクを実装してみます。

例でリソースのツリーは以下のようになっているものとして、商品情報以下のリソースを取得します。

商品情報一覧ページ(ID:2)のリソースに最初から表示しておく一覧用の Ditto を書きます。Ditto は JSON も出力できるので取得してからレイアウトする事も可能ですが、今回はマークアップ済みの HTML を取得する Pjax ライクな表示方法にしました。

<ul class="moreload_pagelist">
[[Ditto?
  &id=`more`
  &parents=`2`
  &display=`4`
  &paginate=`1`
  &tpl=`@CODE: <li><a href="[+url+]">[+title+]<br>登録日: [+date+]</a></li>`
]]
</ul>

ページネーション機能&paginateを有効にしてプレースホルダを使えるようにしておきます。

ページリンクの代わりに以下のような「もっと読む」用のリンクを書きます。@IFタグで次ページが無ければ元からリンクを非表示になるようにしています。

<!--@IF:![+more_totalPages:el(1)+]-->
<div class="moreload_more link" data-totalpages="[+more_totalPages+]" data-perpage="[+more_perPage+]" data-current="[+more_currentPage+]">
  <a href="#" class="link">もっと読む</a><span class="indicator">loading...</span>
</div>
<!--@ENDIF-->

あとで説明する JavaScript では Ajax ロード中は.moreload_moreクラスに.loadingクラスを追加するようにするため、これを利用してロード中なら.moreload_more .loadingを表示、それ以外の時は.moreload_more .linkを表示するようにしました。

.moreload_more { border: 1px solid #ccc; padding: 10px; background: #f5f5f5; text-align: center; }
.moreload_more .link, .moreload_more.loading .indicator { display:block; }
.moreload_more .indicator, .moreload_more.loading .link { display:none; }

次にAjax取得先のリソースを作成します。Ditto はデフォルトで非公開以下のリソースを表示しないので非公開フォルダを作りその中にリソース「ajax.html」を作成します。このリソースはテンプレートを(blank)に設定して Ditto のみを表示するようにします。
「商品情報/app/ajax.html」の中身は以下のとおりで<ul>を省いた以外は HTML に書いたものと同じです。全く同じなのでテンプレートが長ければチャンク化しても良いとおもいます。

[[Ditto?
  &id=`more` &parents=`2` &display=`4` &paginate=`1` &tpl=`@CODE: <li><a href="[+url+]">[+title+]<br>登録日: [+date+]</a></li>` ]]

このリソース自体にアクセスしてページネーション表示されるか確認してみてください。

次に Ajax 用の JavaScript を書きます。jQuery を使用するので jQuery 本体を読み込みます。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

以下サンプルで作成したシンプルな Ajax ロード用の JavaScript コードです。

$(function() {
    // ajax用リソースのURL
    var durl = 'news/app/ajax.html';
    // 結果を表示するコンテナのクラス名
    var e_name_container = '.moreload_pagelist';
    // もっと読む表示用のコンテナセレクタ。このセレクタの中にもっと読むリンク「.link」を入れます。
    var e_name_more = '.moreload_more';

    $(e_name_more + ' .link').on('click', function(event) {
        event.preventDefault();
        var eMore = $(e_name_more);
        $.ajax({
            url: durl + "?more_start=" + parseInt(eMore.attr('data-perpage') * eMore.attr('data-current')),
            dataType: "html",
            beforeSend: function () {
                eMore.addClass('loading');
            }
        }).done(function(data) {
            // 記事を表示するアニメーションはここらへんで調整
            $(data).hide().css({
                opacity: 0
            }).appendTo(e_name_container).slideDown('fast').animate({
                opacity: 1
            }, function() {
                eMore.removeClass('loading');
            });
            eMore.attr('data-current', parseInt(eMore.attr('data-current')) + 1);
            // 読み込む記事がなくなったらmoreを非表示
            if (eMore.attr('data-current') > eMore.attr('data-totalpages') - 1) {
                eMore.hide();
            }
        }).fail(function(a, b, c) {
            console.log(c);
            eMore.removeClass('loading');
        });
    });
});

読み込むタイミングをクリックから.moreloadのスクロール位置(表示位置)に変えてにょごにょしたら Twitter みたいにスクロールしたら勝手に読みこむ感じに出来そうです(?)

そして今更になりますが実はこのタイプの読み込み方法は個人的にはあんまり好きではなくて、数ページ分読み込んでから記事を読んでブラウザで戻るとまた最初からロードしないといけなかったり、そのページ分一覧の URL が取得できない、というのがとても不便に感じます。実際に使う場合はもう少しユーザビリティを考慮した形にするといいかもしれません。例えばはてなブックマークのように通常のページネーションと両実装させるやり方もおすすめです。また時間がある時にそのへんも考慮したサンプルを書きたいなーと思ってます。