fbpx

メニュー

wpdb道場 (12) 〜フォロワーをもっと読み込もう〜

高橋文樹 高橋文樹

この投稿は 9年半 前に公開されました。いまではもう無効になった内容を含んでいるかもしれないことをご了承ください。

前回まででフォロワーを出してみましたが、なんかいまいちスピードが上がらないので、今回はGithubのコミットログを貼り付けて、主に説明したいところまで端折ります。

  1. フォロワーとフォローしている人を取得する関数を修正して1つに
  2. the_contentにかけていたフックの関数をそのまま利用し、Ajaxで返す
  3. それぞれのフォロワーリストにボタンを表示
  4. ボタンにはユーザーIDを渡せるよう変更(指定しなければ現在の投稿の作者IDを利用)

わからないことがあったら、Githubのコミットログにコメントを残してもらえると答えられます。(※ Githubは行ごとにコメント残せるんですよ)

工夫したポイントを説明すると、Ajaxの部分ですかね。

Ajaxを実装する場合、「JSONを取得して云々かんぬん」という説明があるのですが、jsRenderとか、そういうJSテンプレートを使った実装していない限り、JSONで画面を作るのってめんどくさいんですよね。PHP側とJS側両方で似たような描画用関数を実装することになったりして。

WordPressでサイトを作っている場合、わざわざJSONで画面を作るようなWebアプリ的な作り方をしないと思うので、Ajax経由で渡すデータの利用方法が「DOMの操作」なのであれば、HTMLをサーバで作って、それをそのまま渡してしまった方がよいです。

Ruby on Railsの開発者であるDHHがブログでそんなこと(Server-generated JavaScript Responses)を書いていたので、それのしょぼい版ですね。

今回は「これ以上フォロワーが存在しない」という場合、pタグを渡すようにしていて、pタグが挿入された瞬間に「もっと読み込む」ボタンは消えるということをCSS上でやっています。

p.no-follower{
  & + a.fs-more-btn{
    display: none;
  }
}

で、こうなります。下にある「もっと見る」ボタンを押すとそれより古いフォロワーが読み込まれて……

以前と同じく、うまく表示されてます
以前と同じく、うまく表示されてます

あれー!!

読み込んだフォロワーはボタンがグレーのまま
読み込んだフォロワーはボタンがグレーのまま

フォローチェックのイベントを改善する

ページを表示した時点でフォロワーを取得できる機能を以前作りましたが、今回起きている事態は「フォローしているかチェック」機能がAjaxレスポンスでは働かないのが原因ですね。

これを避けるために次のような実装をしましょう。

  1. まず、「ボタンが画面に表示された」というイベントrendered.freundschaftを監視し、まだアップデートされていないボタン(=クラスがfs-disabledのまま)をアップデートする
  2. 「ログインしている/していない」は1回目のチェックでわかるので、1回しか確認しないようにする
  3. イベントが発生するのは、画面の表示(DOMREADY)とAjaxの更新の2回

ちなみに、rendered.freundschaftという書き方はjQueryにおけるイベントの名前空間ですね。clickイベントなんかはよく使いますが、このイベントの監視をやめたいなと思っても、他のJSがclickイベントを監視しているかもしれないんじゃないですか。そういうとき、click.takahashi.fumikiとかで登録しておけば、他のイベントリスナーをそのままに、自分のだけ削除できるんですね。

では、これを以前書いたfollower.jsに実装しましょう。

(function ($) {
    'use strict';

    // いろんなイベントを渡って使う変数を記録
    var nonce = '',
        loggedIn;

    // documentオブジェクトを監視
    $(document).on('rendered.freundschaft', function(){
        var $btns = $('.fs-disabled'), // disabledが未確認のボタンをこの時点で取得
            authorIds = [];
        // ログインしていないことをすでに確認済みだったら、
        // ログインボタンに変更
        if( false === loggedIn ){
            $btns.removeClass('fs-disabled').addClass('fs-login');
        }
        // ボタン全部の投稿者IDを取得して、ログイン判定
        $btns.each(function(index, btn){
            authorIds.push($(btn).attr('data-author-id'));
        });
        // 投稿者IDがあればAjax
        if( authorIds.length ){
            $.post(FreundschaftFollow.endpoint, {
                action: FreundschaftFollow.actions.fs_status,
                author_ids: authorIds
            }).done(function(result){
                if( result.logged_in ){
                    // ログインしているので、ユーザーに応じたボタンを出力
                    for( var prop in result.users ){
                        if( result.users.hasOwnProperty(prop) ){
                            var userId = prop.replace(/user_/, '');
                            if( result.users[prop] ){
                                // フォローしている
                                $('.fs-btn[data-author-id=' + userId + ']').removeClass('fs-disabled').addClass('fs-following');
                            }else{
                                // フォローしていない
                                $('.fs-btn[data-author-id=' + userId + ']').removeClass('fs-disabled').addClass('fs-follow');
                            }
                        }
                    }
                    // nonceを保存
                    nonce = result.nonce;
                }else{
                    // ログインしていないので、
                    // ボタンをログインに
                    $btns.removeClass('fs-disabled').addClass('fs-login');
                    // ログインチェックを記録
                    loggedIn = false;
                }
            }).fail(function(xhr, status, message){
                alert(message);
            });
        }
    });

    // DOMREADYでイベント発行
    $(window).ready(function(){
        $(document).trigger('rendered.freundschaft');
    });

    $(document).on('click', '.fs-btn', function(e){
        // ここは変更なしなので中略
    });

    // フォロワーをもっと読み込むボタン
    $(document).on('click', '.fs-more-btn', function(e){
        e.preventDefault();
        var $btn = $(this),
            endpoint = $btn.attr('href') + '&offset=' + $btn.attr('data-offset');
        // 読み込み中は何もしない
        if( !$btn.hasClass('loading') ){
            // 読み込み中に変更
            $btn.addClass('loading');
            $.get(endpoint).done(function(result){
                // HTMLを挿入
                $btn.before(result.html);
                // オフセットを更新
                $btn.attr('data-offset', result.offset);
                // ボタンの更新イベント
                $(document).trigger('rendered.freundschaft');
            }).fail(function(xhr, status, error){
                alert(error);
            }).always(function(){
                // 読み込み中を解除
                $btn.removeClass('loading');
            });
        }
    });

})(jQuery);

変わったところだけハイライトしようかと思いましたが、大変なのでGithubのコミットdiffを見てください。こんだけコード量が多くなってくると、githubの方が見やすいですね……。

こんな感じになりました
こんな感じになりました

まとめ

  • Ajaxでどんどん読み込んだ
  • Ajaxで返すJSONにHTMLぶっこんじゃいなよ!
  • DOMREADY以外でも画面を更新した

さて、というわけで、色々とやってきましたが、他の細部を(フォロワー数が出てない)とか、そいういう問題もありつつ、本来の目的は$wpdbに詳しくなることなので、次回以降、細部の調整をしつつ、メッセージ機能の仕様を固めてみましょう。

[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)

すべての投稿を見る

高橋文樹ニュースレター

高橋文樹が最近の活動報告、サイトでパブリックにできない情報などをお伝えするメーリングリストです。 滅多に送りませんので、ぜひご登録お願いいたします。 お得なダウンロードコンテンツなども計画中です。