WP10は弊社が掲げる「WordPressを使った10倍速開発」です。
10倍とは、フルスクラッチに比べて10倍ということです。
この連載ではWP開発に長く携わっている弊社のWordPress開発ベストプラクティスを発信していきます。
第二回は「ACFでJavaScriptのフックを使って、ACFの繰り返しフィールドの内容をコピーする」です。ACFとはAdvanced Custom Fieldsで、入稿画面を自由度高くカスタマイズできるPluginです。ノンコーディングで表側を作成することができるので、WordPressの高速開発を掲げるWP10ではマストなPluginです。
ACFではacf/save_postやACFのフィールドキーを使用する独自のフックがあります。あまり知られていませんが、実はACFはJavaScriptにも独自のフックが用意されています(公式ドキュメント)。
今回はappendというフックの使用方法を説明します。appendは「新しいHTMLが追加されたときに作動する」フックです。

作るもの

本の著者を説明するリピーターフィールドを作成すると仮定します。リピーターフィールドの項目は「画像」「タイトル」「著者」にしています。
しかし、「夏目漱石」を2度入力するのは煩雑です。そんな時はappendのフックを使って入力を楽にしましょう!
仕様
- 「行をコピー」のボタンを作成する
- 「行をコピー」をクリックすると、著者名が入った状態で新しい行が作成される
では実際に作成してみましょう。
「行をコピー」ボタンを追加する
var $copyBtn = $('<li><a class="acf-button button button-primary copy" data-event="add-row">行をコピー</a></li>');
$('ul.acf-actions.acf-hl').append($copyBtn);
JavaScriptでコピーボタンを用意し、要素をDOMに追加します。すると、「行を追加」ボタンの左に「行をコピー」ボタンが表示されました!

「コピー」ボタンをクリックすると、著者名がコピーされる
// ACFのJSにフック
acf.add_action( 'append', function( $el2 ){
// 「行をコピー」ボタンを押した時のみ、コピーされる
if ( $el2.context.innerHTML !== '行をコピー' ) {
return;
}
var tagInput = 'input';
// コピー元のフィールドを取得
var $prevField = $('div.acf-repeater tr:nth-last-of-type(3)');
var $prevInputs = $prevField.find(tagInput);
// 追加されたフィールドを取得
var $nextField = $el2;
var $nextInputs = $nextField.find(tagInput);
// 著者名をコピーする
let $next = $($nextInputs.get(2));
let $prev = $($prevInputs.get(2));
$next.val( $prev.val() );
});
「行をコピー」をクリックすると、新しいリピーターフィールドのDOMが追加されます。ACFのappendフックが発火し、acf.add_actionで作成した関数が作動します。これにより、前の行に入力されている「著者」の文字列が新しい行にコピーされました。

コメント「著者名をコピーする」で$($prevInputs.get(1))とすると、「タイトル」に入力された文字列が新しい行にコピーされます。
(発展編1)画像をコピーする
// 「行をコピー」ボタンを押した時のみ、コピーされる
if ( $el2.context.innerHTML !== '行をコピー' ) {
return;
}
var classAcfImgUploader = '.acf-image-uploader';
// 追加されたフィールドを取得
var $nextField = $el2;
var $nextImgUploader = $nextField.find(classAcfImgUploader);
// コピー元のフィールドを取得
var $prevField = $('div.acf-repeater tr:nth-last-of-type(3)');
var $prevImgUploader = $prevField.find(classAcfImgUploader);
// 画像をコピーする
let $next = $($nextImgUploader.get(0));
let $prev = $($prevImgUploader.get(0));
// 画像が挿入済みである時のクラスを付与
if( $prev.hasClass('has-value') ) {
$next.addClass('has-value');
}
var $nextImg = $next.find('[data-name="image"]');
var $prevImg = $prev.find('[data-name="image"]').attr('src');
$nextImg.attr('src', $prevImg);
});
画像をコピーする場合は少し特殊です。
画像は「acf-image-uploader」というクラスが付与されています。このため、inputタグで入力した内容とは要素の取得の方法が異なります。上記のコードを実行した結果は下記のようになり、同じ画像を選択する手間を省くことができます。

(発展編2)全ての要素をコピーする(コードの全体像)
jQuery(function( $ ) {
// 「行のコピー」追加
var $copyBtn = $('<li><a class="acf-button button button-primary copy" data-event="add-row">行をコピー</a></li>');
$('ul.acf-actions.acf-hl').append($copyBtn);
// ACFのJSにフック
acf.add_action( 'append', function( $el2 ){
// 「行をコピー」ボタンを押した時のみ、コピーされる
if ( $el2.context.innerHTML !== '行をコピー' ) {
return;
}
var tagInput = 'input';
var classAcfImgUploader = '.acf-image-uploader';
// 追加されたフィールドを取得
var $nextField = $el2;
var $nextInputs = $nextField.find(tagInput);
var $nextImgUploader = $nextField.find(classAcfImgUploader);
// コピー元のフィールドを取得
var $prevField = $('div.acf-repeater tr:nth-last-of-type(3)');
var $prevInputs = $prevField.find(tagInput);
var $prevImgUploader = $prevField.find(classAcfImgUploader);
// テキストやチェックボックスをコピーする
for ( let i = 0, len = $prevInputs.length; i < len; ++i ){
let $next = $($nextInputs.get(i));
let $prev = $($prevInputs.get(i));
$next.val( $prev.val() );
}
// 画像をコピーする
var $next = $($nextImgUploader.get(0));
var $prev = $($prevImgUploader.get(0));
// 画像が挿入済みである時のクラスを付与
if( $prev.hasClass('has-value') ) {
$next.addClass('has-value');
}
var $nextImg = $next.find('[data-name="image"]');
var $prevImg = $prev.find('[data-name="image"]').attr('src');
$nextImg.attr('src', $prevImg);
});
});
コードの全体像です。このコードを記述した状態で「行をコピー」ボタンをクリックすると、直前の行と全く同じ繰り返しフィールドが作成されます。

終わりに
いかがでしたでしょうか。ACFは他にも便利なJavaScriptのフックがあるので、ぜひ活用してみてください!
次回は「ユーザー一覧画面にカラムを追加する方法」をご紹介します!