JIN:RでjQueryの読み込みの下にJS(JavaScript)を読み込む
JIN から JIN:R に変えて、記事の移行が進んできました。
今回、移行ツールには頼らず gutenberg のエディタの使い方を覚えながらの設定。
個人的に最大の難関かなと思っていたのは、JavaScript による非同期通信。
Ajax で WordPress のサーバと通信する機能を使っているので、そのあたりが柔軟にできるかが心配でした。
今回は JIN:R で、プラグインを使わずに非同期通信を実装してみたので紹介します。
これまでの非同期通信
JIN の頃は、記事単位で JavaScript や html が差し込める、「Scripts n Styles」というプラグインを使っていました。
また、head 内のスクリプトを html の最後に持ってくるように調整もしていました。
記事単位で読み込ませたい JavaScript を変更するとなると、やはりプラグインに頼りたくなるところです。
JIN:Rの非同期通信
JIN:R も jQuery は読み込んでいるのですが、この jQuery の読み込みよりも下に目的の JavaScript が差し込めずにハマりました。
また、「Scripts n Styles」のプラグインを入れてもうまくいかない。
これはちょっと厄介ということで、functions.php を触ることに。
やはり「子テーマ」にしておいて良かったですね。
では下のコードの説明をしていきます。
add_action('wp_enqueue_scripts', 'my_wp_enqueue_scripts');
function my_wp_enqueue_scripts() {
if (is_single(['hoge', 'fuga'])) {
$slug = get_post_field('post_name', get_the_ID());
$path = get_stylesheet_directory() . sprintf('/js/%s.js', $slug);
wp_enqueue_script(
$slug,
get_stylesheet_directory_uri() . sprintf('/js/%s.js', $slug),
['jquery'],
file_exists($path) ? filemtime($path) : false,
true
);
wp_localize_script($slug, 'hogeAjax', [
'url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hoge_ajax'),
]);
}
}アクションに関数を登録
WordPress に慣れていないとわからないのですが、add_action も一度覚えてしまえばこんなものなのかなってところですね。
ここでは「wp_enqueue_scripts」のアクションフックに、「my_wp_enqueue_scripts」の関数を登録します。
このまま add_action の 2 番目の引数にコールバック関数として入れ込むこともできますが、コールバック関数は別で定義してみました。
この中で、特定のスラッグ(slug)のページだけ JavaScript を読み込ませるだけですね。
スラッグ名と JavaScript のファイル名を合わせておけば少し楽かな。
そしてこの記事の本題である「jQueryの下に読み込ませる」を実現しているのは、第3引数の ['jquery']です。
ここに依存関係を書いておくと、WordPress が必ず jQuery を先に読み込んでから自分のJSを出力してくれます。第5引数の true(フッターで読み込む)だけでは、読み込み順は保証されません。
あわせて wp_localize_script で、admin-ajax.php のURLと nonce をJS側へ渡しています。URLを直接書かずに admin_url() から渡しておくと、ドメインが変わっても直す必要がありません。
第4引数の filemtime() はファイルの更新時刻をバージョンに使う書き方で、JSを差し替えたときにブラウザが古いキャッシュを掴み続けるのを防げます。
Ajax通信をトリガーに呼ばれる関数
Ajax 通信をトリガーに呼び出される関数も、add_action で定義。
これは、ログインユーザと非ログインユーザで別になるので、以下の 2 つのアクションを定義して、hoge_ajax_call の関数を作成します。
add_action('wp_ajax_hoge_ajax', 'hoge_ajax_call');
add_action('wp_ajax_nopriv_hoge_ajax', 'hoge_ajax_call');
function hoge_ajax_call() {
check_ajax_referer('hoge_ajax', 'nonce');
$id = isset($_POST['id']) ? sanitize_text_field(wp_unslash($_POST['id'])) : '';
$name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
if ($id === '') {
wp_send_json(['error' => true, 'info' => 'IDが指定されていません。']);
}
wp_send_json(['error' => false, 'info' => $name . ' さん (ID: ' . $id . ') を受け付けました。']);
}ここで大事なのが、ハンドラの中で必ずレスポンスを出力して終わることです。
admin-ajax.php は最後に wp_die('0') を実行するので、何も出力せずに関数を抜けるとレスポンスが文字列の 0 になってしまいます。JS側で data.info を見ているのに何も表示されない、というときは大体これが原因です。
wp_send_json() を使えばJSON形式で出力して終了までしてくれるので、wp_die() を別途書く必要はありません。
また wp_ajax_nopriv_ はログインしていない人からも叩ける入口です。check_ajax_referer() で nonce を検証し、受け取った値は sanitize_text_field() を通してから使うようにしておきましょう。
呼び出し側の JavaScript は、jQuery のライブラリを使うのですが、JIN:R の場合は $(ダラー)ではなく jQuery の変数を使わないとダメっぽいですね。
これは WordPress が jQuery を noConflict モードで読み込んでいるためで、グローバルの $ が使えません。jQuery(function($) { ... }) の形にしておけば、その中では今までどおり $ が使えます。
かなり雑に書きましたが、こんなイメージになるかな。
jQuery(function($) {
$(document).on('click', '#js_hoge', function() {
let id = $('#js_id').val();
let name = $('#js_name').val();
$.ajax({
type: "POST",
url: hogeAjax.url,
dataType: "json",
data: {
"action": "hoge_ajax",
"nonce": hogeAjax.nonce,
"id": id,
"name": name
},
success: function(data) {
if (data.error) {
alert(data.info);
return false;
} else {
$('#js_response').html(data.info);
return false;
}
},
error: function() {
alert('error.');
return false;
}
});
});
});