tips

wordpressのコメント欄をajax化する。

ajaxfy_comment

プラグインに頼らずテーマ内で完結するAJAXコメントフォーム。

前回に引き続き、コメントの周りのカスタマイズ。

一昔前にQuick Commentsというのがあって、いまはAjax CommentsやWP Ajaxify Commentsというのもあって、でも環境依存の問題か上手く動作しなかったり、もっさりしていたりで、いくつもある他のプラグインの動作確認をやるのもしんどいしで、どうにも…あ゛ーって感じになったので、自作してみたら意外とすんなりまとまった、と。

※2015/2/25 …. undefinedを区別するようにjsを修正しました。このコードはAJAXを使用しているので、htmlタグが誤っていたり、複雑な構造になっている場合、あるいは他のjs系プラグインを使用している場合、干渉しあって期待通りの動作をしないこともあります。十分に注意してお使いください。

 

CODE: Ajaxify WordPress comments

<?php
// コメントフォームのAJAX化
function ajaxify_comments_script(){
if ( is_singular() && comments_open() )://固定や投稿等シングルページで動作、場所によっては要変更
?>
<script id="comment-script" type="text/javascript">
jQuery(function( $ ){

    var commentbox_name = '#comments', //コメントテンプレート名、カスタマイズ時は要変更
        commentform_name = '#commentform', //コメントフォーム名、カスタマイズ時は要変更
        commentbox = $( commentbox_name ),
        commentform = $( commentform_name, commentbox );
    ajaxCommentsTrigger();

    function ajaxComments( this_form ){
        var statusdiv = $('<div class="comment-ajax"></div>'); // AJAXの結果を吹き出す窓、デザインはCSSで指定してね
        this_form.append( statusdiv ).submit( function (e) {
            var formdata = this_form.serialize(), // フォームをシリアライズ
                formurl = this_form.attr('action'); // フォームのactionに設定されたurl
            e.preventDefault();
            statusdiv.html('<div class="ajax-processing">コメントを送信中…</div>'); // 送信されると.comment-ajax内に表示
            $.ajax({ 
                type: 'post',
                url: formurl,
                data: formdata
            }).done(function(data,status){
                if( status == "success" ){
                    var matches = data.match(/<body+s+id="error-page">([sS]*?)</body>/);
                    if ( matches ) {
                        var output = $('<div class="ajax-error">' + matches[1] + '</div>');
                        output.find('*:empty').remove();
                        output.find('*:first-child').siblings().remove();
                        statusdiv.html( output );
                        return false;
                    }
                    var newlist = $( data ).find( commentbox_name );
                    if ( newlist.length ){ 
                        statusdiv.html('<div class="ajax-success">コメントありがとうございました。</div>');
                        reply = this_form.parents( 'li.depth-1' ).attr( 'id' );
                        if ( typeof reply !== "undefined" ) {
                            window.location.hash = reply;
                            statusdiv.find( '.ajax-success' ).stop().delay( 500 ).slideUp( 200,function(){
                                commentbox.replaceWith( newlist );
                                commentbox = $( commentbox_name );
                                ajaxCommentsTrigger();
                                $('html, body').animate({
                                    scrollTop: $( '#' + reply ).offset().top
                                }, 500);
                            });
                        }
                    } else {
                        statusdiv.html('<div class="ajax-error">エラーが発生しました。ウェブマスターにご連絡ください。</div>');
                        return false;
                    }
                } else {
                    statusdiv.html('<div class="ajax-error">エラーが発生しました。時間をおいて再度送信して下さい。</div>');
                    return false;
                }
            }).fail(function(data){
                statusdiv.html('<div class="ajax-error">送信が中断されました。<strong>記入もれや間違いがないか</strong>再度ご確認ください。</div>');
            });
        });
    }

    function ajaxCommentsTrigger(){
        var replybox = $( '<div id="reply-box"></div>' ),
            commentform = $( commentform_name, commentbox );
        commentform.clone( true ).appendTo( replybox );
        replybox.find( '[id]' ).each(function(){
            $(this).prop('id', 'reply-' + $(this).attr( 'id' ) );
        });
        ajaxComments( commentform );
        ajaxComments( replybox.children( 'form' ) );
        $( 'a.comment-reply-link', commentbox ).click(function(e){ 
            e.preventDefault();
            var replylink = $(this), list = replylink.parents( 'li.depth-1' );
            if ( list.hasClass( 'replying' ) ){
                list.removeClass( 'replying' );
                replylink.html( '返信する' );
                replybox.slideUp( 500 );
            } else {
                list.addClass( 'replying' );
                list.siblings('.replying').removeClass( 'replying' ).find( 'a.comment-reply-link').html( '返信する' );
                replylink.html( '返信をキャンセル' );
                var parentid = list.attr('id').slice(8); // ex:#comment-999
                replybox.find( '#reply-comment_parent' ).val( parentid );
                replybox.hide().appendTo( list ).slideDown( 500 );
            }
        });
    }
});
</script>
<?php
endif;
}
add_action('wp_footer', 'ajaxify_comments_script');
?>

このサンプルコードではfunctions.phpに記載することを前提にwp_footerにフックしてますが、jQueryだけで完結してるんでjs部分をテンプレートに直書きしてもOKです。動作はここのフォームでコメントなしで送信ボタンを押せば、エラーが返ってきますのでどうぞ。ちなみに前記事「Really Simple CAPTCHAをコメント欄に応用する。」も併用できます。

 

どうやってヴァリデートするか、が肝なのかも。

ajaxでpostするところまではどのプラグインでも動作は大体同じ。コメントのヴァリデーション方法については作者毎に個性があるが、comment_postにフックしてajax時のみ処理を分岐させて云々…というのが常道のようだった。それで良いのだけども、結局、コメント投稿に失敗したときに返ってくるエラーページを読み込んでPOSTの可否を判断することにした。htmlソースで判断することの是非はあるかもしれないが、フック処理が不要になるしwordpressのエラーメッセージを流用できるので、関数をシンプルに保てて、保安上もメリットになると思えたからだ。

もうひとつの大きな設計判断としては、コメント後にユーザーにだけ見えるモデレート待ち のコメントを表示する方法をどうするかだけども、これはコメントが通過後に返ってくる新しいポストから#commentsタグを抽出して丸ごと差し替えること にした。キャプチャを使用している場合にはその更新が必要だったり、コメントテンプレートのデザインやソートがテーマ毎にまちまちだったりするので、コメントテンンプレートをまるごと差し替えるのが現実的だろうというわけです。

おまけ:ここで適用してるcss

ajaxは思いの外時間がかかることもあるので、css 3のアニメーションなどを活用してユーザーを迷わせないよう設計することが大事ですね。

.comment-ajax { margin:5px 0; }
.comment-ajax > div { padding:5px 10px; font-size:16px; }
.ajax-error { background:#356; color:#fff; }
.ajax-success { background:#2bb; color:#fff; } 
.ajax-processing { position:relative; background:#eee; } 
.ajax-processing:before { width:100%; display:block; content:' '; margin:0; padding:0; background:#a9e3e4; position:absolute; height:3px; left:0; bottom:0; -webkit-animation:fullexpand 1.5s ease-out; z-index:1; animation:fullexpand 1.5s ease-out; }
@-webkit-keyframes fullexpand { 0%  { width:0;} 100%{ width:100%;} }
@keyframes fullexpand { 0% { width:0;} 100%{ width:100%;} }

Your Comment

コードの記述は<pre>または<code>タグで括って下さい。自動的にエスケープされます。

 

右の文字を入力して下さい captcha

18 comments

  1. 匿名

    Nov 15, 2016

    テストですう

    返信する
    • test

      Feb 4, 2017

      テストさせていただきます。

  2. test

    Nov 9, 2016

    成功するときと失敗するときがあり、失敗するとURLの末尾に「#undefined」が付いて、commentsのdivがごっそり消え去ります。

    どうすれば。。。。

    返信する
    • Mizuho Ogino

      Feb 25, 2017

      ご報告ありがとうございます。

      undefinedを区別するように上記のjsを修正しました。

      commentsのdivが消えるのは、ポストはうまくいったにもかかわらずjsの処理が失敗しているせいです。
      undefinedの発生原因は、ajaxの読み出し先に誤ったhtmlタグなどがあるって失敗している可能性が高いのではないかと推測します。処理はphpなのでコメントがなくなるわけではないです。動作が改善しない場合は、テンプレートを見直すか、コメントIDを取得するタイプのajaxプラグインを使う必要があるかもしれません。
      undefinedが帰ってきた場合、ページをロードするようなjsの仕掛けをするのもいいかもしれませんね。承認待ちなどのシステムを活用していると、コメントが現れず不安を覚えて重ねて投稿、ということもあるかもしれませんので。そのうち考えます。

  3. test

    Nov 9, 2016

    一番最初のコメント投稿だと、#commentsごと消えてしまいます。。。

    返信する
  4. test

    Jul 15, 2016

    testさせてください

    返信する
    • test

      Jul 23, 2016

      テストさせてください

    • 匿名

      Sep 23, 2016

      テスト

    • test222

      Aug 17, 2016

      test22

    • eee

      Aug 25, 2016

      test

    • 匿名

      Sep 23, 2016

      tss

    • test

      Nov 8, 2016

      テストさせていただきます

  5. k

    Mar 1, 2016

    テスト投稿させてください。

    返信する
    • 匿名

      Jul 12, 2016

      test

    • お試し

      Jul 13, 2016

      test

  6. かず

    Jan 9, 2016

    こんにちは!

    素晴らしいコードありがとうございます!
    一つ質問があるのですが、メッセージを表示させる場所を変更するには
    どうしたら良いのでしょうか?
    お手すきの間にでもご教示頂けると幸いです。

    返信する
    • Mizuho Ogino

      Jan 9, 2016

       var statusdiv = $('<div class="comment-ajax"></div>'); this_form.prepend( statusdiv ).......

      上記の箇所で、div.comment-ajaxをフォームに対してprepend(先頭)に挿入しています。appendに変更してフォームの後方に入れるか、そもそもjsは触らなくて済むように、cssでポジションを指定するのが一番簡単な改変方法です。

    • かず

      Jan 9, 2016

      さっそくの返信ありがとうございます!

      appendにしてさらにCSSで調整することにしました。
      JSは全然わからなかったので助かりました〜m(_ _)m
      これを機に勉強してみようと思います。
      ありがとうございます。