前回まででフォロワーを出してみましたが、なんかいまいちスピードが上がらないので、今回はGithubのコミットログを貼り付けて、主に説明したいところまで端折ります。
- フォロワーとフォローしている人を取得する関数を修正して1つに
- the_contentにかけていたフックの関数をそのまま利用し、Ajaxで返す
- それぞれのフォロワーリストにボタンを表示
- ボタンにはユーザー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レスポンスでは働かないのが原因ですね。
これを避けるために次のような実装をしましょう。
- まず、「ボタンが画面に表示された」というイベント
rendered.freundschaft
を監視し、まだアップデートされていないボタン(=クラスがfs-disabled
のまま)をアップデートする - 「ログインしている/していない」は1回目のチェックでわかるので、1回しか確認しないようにする
- イベントが発生するのは、画面の表示(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に詳しくなることなので、次回以降、細部の調整をしつつ、メッセージ機能の仕様を固めてみましょう。
シングルページWebアプリケーション ―Node.js、MongoDBを活用したJavaScript SPA
価格¥4,699
順位630,530位
著Michael S. Mikowski, Josh C. Powell
監訳佐藤 直生
翻訳木下 哲也
発行オライリージャパン
発売日2014 年 5 月 24 日