[WordPress] 投稿をAjaxで読み込む時にプラグインのShortCodeが発動しない時のTips

# はじめに
WordPressテーマ開発をしている中で、

* WordPressの投稿をAjaxで取得
* 何かしらプラグインのショートコードを`do_shortcode()`する

っていうことは、それぞれよくやると思う。でもこの2つがかけ合わさった時に色々と手こずったので知見をメモしておきます。せっかく早起きして優雅に始まった朝がめちゃニッチなデバッグに消えたので、この記事で他の誰かの優雅な朝を守りたい。

ちなみに自分が手こずったのは[AddToAny Share Buttons](https://ja.wordpress.org/plugins/add-to-any/)というプラグインで、なぜかAjax経由だとアイコンが表示されない。最終的にはプライグイン独自の仕様の問題だったのでとにかく解決策を知りたい方は[最終セクション](#addtoanyをajaxで読み込む時は)を見ると良いです。

# WordPressでAjaxを使う時のお作法
そもそもWordPressでAjaxを使う時にはお作法が決まっていたので基本としてメモ。

### Ajaxリクエストを投げる場所
WordPressにはAjaxリクエストを処理するために`wp-admin/admin-ajax.php`というファイルがあり([Reference](https://developer.wordpress.org/reference/files/wp-admin/admin-ajax.php/))、原則としてリクエストはここに投げるような仕様になっている。もちろん独自にajax用のファイルを用意しても出来るけど、`admin-ajax.php`にリクエストすると、`functions.php`で登録した関数が使えるようになるので便利。`admin-ajax.php`のファイルurlは

```functions.php
<?php
function add_my_ajaxurl() {
?>
  <script>
    const ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
  </script>
<?php
}
add_action('wp_head', 'add_my_ajaxurl', 1);
```

としてヘッダの読み込みにフックしてグローバル変数として登録しておけば、どこのテーマファイルでも使えるようになる。

### Ajaxリクエスト時に実行するactionの登録
Ajaxリクエスト時、`action`という名称で実行するactionを指定することができる。

```my-ajax.js
$.ajax({
    type: 'POST',
    url: ajaxurl,
    data: {
        action: 'my_ajax_action',
    },
    success: function(res) {
        // 成功時の処理
    }
});
```

ここでの`my_ajax_action`が自分で登録したactionになる。登録は`functions.php`にて、

```functions.php
add_action('wp_ajax_my_ajax_action', 'my_ajax_action');
add_action('wp_ajax_nopriv_my_ajax_action', 'my_ajax_action');

function my_ajax_action() {
    // 任意の処理
}
```

として記述する。actionが2種類あるが`nopriv`修飾子がついているのは非ログインユーザーに対する処理([Reference](https://developer.wordpress.org/reference/hooks/wp_ajax_nopriv_action/))。ログイン/非ログインで特別に処理を分けたい時以外は両方に同じものを登録しておく。ちなみに`action`引数が参照するのは関数名ではなくて、`wp_ajax_`, `wp_ajax_nopriv_`の後の文字列なので注意。

### Ajaxを用いた投稿読み込みのサンプル
一応サンプルを示しておく。

```functions.php
<?php
//---------------------------------
// subscript ajax url
//---------------------------------
function add_my_ajaxurl() {
?>
  <script>
    const ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
  </script>
<?php  
}
add_action('wp_head', 'add_my_ajaxurl', 1);

//---------------------------------
// get post via ajax
//---------------------------------
add_action('wp_ajax_my_get_post_data', 'my_get_post_data');
add_action('wp_ajax_nopriv_my_get_post_data', 'my_get_post_data');

function my_get_post_data() {
  $offset         = isset( $_POST['post_num_now'] ) ? $_POST['post_num_now'] : 1;
  $posts_per_page = isset( $_POST['post_num_add'] ) ? $_POST['post_num_add'] : 0;

  $ajax_query = new WP_Query(
    array(
      'post_type'      => 'post',
      'posts_per_page' => $posts_per_page,
      'offset'         => $offset,
    )
  );

  if ( $ajax_query->have_posts() ) {
    while ( $ajax_query->have_posts() ) {
      $ajax_query->the_post(); ?>
          <div class="post">
            <a href="<?php echo get_the_permalink(); ?>">
                <?php the_post_thumbnail('medium'); ?>
                <p><?php the_time('Y.m.d'); ?></p>
                <h2><?php the_title(); ?></h2>
            </a>
          </div>
    <?php }
  }
  wp_reset_postdata();
  exit;
}
```

```ajax-get-posts.js
let postNumNow = 0;
let postNumAdd = 5; // moreで何件表示するか
let flag = false;

function addPosts() {
    if(!flag) {
        flag = true;
        $.ajax({
            type: "POST",
            url: ajaxurl,
            data: {
                action: 'my_get_post_data',
                post_num_now: postNumNow,
                post_num_add: postNumAdd
            },
            success: function(response) {
                let $postWrap = $('<div class="post_wrap"></div>');
                $('.posts').append($postWrap);
                $postWrap.append(response);
                $postWrap.fadeIn(800);
                postNumNow += postNumAdd;
                flag = false;
            }
        });
    }
}

// 表示の最下部まで到達したら次を読み込む
$(document).ready(function() {
    $(window).on('load', addPosts); // 初期表示
    $(window).scroll(function() {
        let postsBottom = $('.posts').offset().top + $('.posts').innerHeight();
        let scrollTop = $(this).scrollTop();
        if(scrollTop > postsBottom - $(window).innerHeight()) addPosts();
    });
});

```

# Ajax経由で`do_shortcode('hoge')`が文字列として表示される時の対処法
さて本題。Ajaxで読み込むリクエスト先に`do_shortcode`を挿入したい時。
例えば

```functions.php
function my_ajax_action() {
    if(have_posts()) {
        while(have_posts()) {
            the_post();
            echo do_shortcode('[addtoany]');
        }
    }
}
```
のように投稿内で呼び出すと、do_shortcodeがそのまま文字列として表示される場合がある。今回はこれとは症状が異なったので解決策にはならなかったが、この場合の解決策がStackExchangeに載っていたので共有する →[Why might a plugin's 'do_shortcode' not work in an AJAX request?](https://wordpress.stackexchange.com/questions/53309/why-might-a-plugins-do-shortcode-not-work-in-an-ajax-request)

簡単にいうと、Ajaxリクエストが`admin-ajax.php`を経由して処理されるので、`wp-includes`に存在する`shortcodes.php`にアクセスできないので、自前でショートコードを登録必要があるとのこと。(今回はこれに関しては大丈夫だったので解決されたのかな??もしくは特定の条件下で起きるのか...要検証)

念のためコードを載せておく。

```functions.php
add_action( 'init', function() { 
  ps_register_shortcode_ajax( 'ps_get_survey_form', 'ps_get_survey_form' ); 
} );

function ps_register_shortcode_ajax( $callable, $action ) {

  if ( empty( $_POST['action'] ) || $_POST['action'] != $action )
    return;

  call_user_func( $callable );
}

function ps_get_survey_form() {
    echo do_shortcode( '[contact-form-7 id="397" title="contact form 1"]' );
    die(); 
} 
```

# AddToAnyをAjaxで読み込む時は
そして結局(私の場合の)解決策はこれ。最終的にはAddToAny Share Buttonsの[API Reference](https://www.addtoany.com/buttons/api/)に答えがありました。やっぱ公式Reference大事です。。本当に
> By default, AddToAny's page.js script initializes & renders all AddToAny HTML instances when the document is ready.

>AddToAny JavaScript functions can be used to initialize & render new AddToAny HTML instances inserted after the document is ready.

>The a2a.init_all() function is often used after content insertion via Ajax.

つまり普通に`archive.php`とか`single.php`で呼び出せばちゃんと初期化して表示するんだけど、Ajaxみたいに後から読み込む場合は初期化&描画する関数`a2a.init_all()`用意したから実行してね、とのこと。実行はAjax responce受け取って描画した後に挿入したら行けました。

```ajax-get-posts.js
function addPosts() {
    if(!flag) {
        flag = true;
        $.ajax({
            type: "POST",
            url: ajaxurl,
            data: {
                action: 'my_get_post_data',
                post_num_now: postNumNow,
                post_num_add: postNumAdd
            },
            success: function(response) {
                let $postWrap = $('<div class="post_wrap"></div>');
                $('.posts').append($postWrap);
                $postWrap.append(response);
                a2a.init_all();    // ← ここです！！
                $postWrap.fadeIn(800);
                postNumNow += postNumAdd;
                flag = false;
            }
        });
    }
}
```

### 終わり

ちょっとニッチな内容でしたが誰かの助けになると良いですね:) ここまで読んでくださってありがとうございます。何かご指摘あったらどしどしお寄せください。