【WordPress】テーマカスタマイザーで複数選択できるチェックボックスを自作する【jQuery不要】

Takashi Fujisakiのアバター
7400文字

WordPressのテーマカスタマイザーをカスタマイズする際に、複数選択できるチェックボックスが欲しいときって結構あるんですが、WordPress5.5時点では公式に用意されていないようです。というわけで自作の方法を紹介します。日本語の解説記事があまりないので残しておきます。

作りたいもの

こういう複数チェックできる項目を作りたい

WordPressカスタマイザーをカスタマイズしてると、こんな感じ↑の複数の項目を選択できるやーつ、結構欲しくなりません??ですが、なぜかデフォルトではマルチセレクトの項目は用意されていません。でも必要なものは必要なので作っちゃおうというお話です。作った途端公式で実装されるってのはよくある話ですが。

ここで紹介するものは基本コピペでいけますが、読み込みファイルのパスの指定などは環境に合わせて変更してください。

手順1 複数選択チェックボックスのカスタムクラスを定義

まずは新しいPHPファイルを作って、複数選択チェックボックス用のクラスを定義します。タイプ名と出力HTMLを定義します。コピペでOK。

<?php
// 複数選択できるチェックボックスを定義
class My_Customize_Multiple_Checkbox_Control extends WP_Customize_Control {

  public $type = 'multiple-checkbox';

  protected function render_content() {
    if ( empty( $this->choices ) ) return; ?>

    <?php if ( !empty( $this->label ) ) : ?>
      <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
    <?php endif; ?>

    <?php if ( !empty( $this->description ) ) : ?>
      <span class="description customize-control-description"><?php echo esc_html( $this->description ); ?></span>
    <?php endif; ?>

    <?php $multi_values = !is_array( $this->value() ) ? explode( ',', $this->value() ) : $this->value(); ?>

    <ul>
      <?php foreach ( $this->choices as $value => $label ): ?>
      <li>
        <label>
          <input type="checkbox" value="<?php echo $value; ?>" <?php checked( in_array( $value, $multi_values ) ); ?>>
          <?php echo esc_html( $label ); ?>
        </label>
      </li>
      <?php endforeach; ?>
    </ul>
    <input
      type="hidden"
      name="mulitiple-checkbox-value"
      <?php $this->link(); ?>
      value="<?php echo esc_attr( implode( ',', $multi_values ) ); ?>"
    >
  <?php
  }
}

いろいろ試しましたが、複数の値を取り扱うためにはひと工夫が必要です。

具体的には、hidden要素 (ここでは input[name="multiple-checkbox-value"]) を用意して、複数の選択された項目の値をhidden要素の値に集約します。さらにこのhidden要素に $this->link() を記述し、カスタマイザーとの値の連携役にします。

hidden要素の値は文字列でなければならないので、チェックされた複数の項目の値をカンマ区切りで並べた文字列にします。それも、項目のチェック状態によってリアルタイムで変わる必要があります。もちろんこれは素のHTMLでは実現できないのでJavaScriptの出番となります。

データベースに保存する際は文字列のままではなく、扱いやすいように配列形式で保存したいので、後述のサニタイズ関数を使って実現します。

手順2 選択された複数の値を扱うためのJavaScript

新しいJavaScriptファイルを作って、以下をコピペします。

window.onload = function () {
  const $parents = document.querySelectorAll('.customize-control-multiple-checkbox')

  $parents.forEach((parent) => {
    const $checkboxes = parent.querySelectorAll('input[type="checkbox"]')
    const $hidden = parent.querySelector('[name="mulitiple-checkbox-value"]')

    $checkboxes.forEach((checkbox) => {
      checkbox.addEventListener('change', (event) => {
        const $checked = parent.querySelectorAll('input[type="checkbox"]:checked')
        const values = [...$checked].map((node) => {
          return node.value
        })

        $hidden.value = values.join(',')
        $hidden.dispatchEvent(new Event('change'))
      })
    })
  })
}

記事の最後に掲載した参考記事ではjQueryで書いてあったんですが、jQueryは卒業したかったのでネイティブJSで書いてみまました。

やりたいことは、項目のチェックをオンオフするたびに input[name="multiple-checkbox-value"] の値をリアルタイムに変更すること。値はカンマ区切りの文字列。

12行目は map を使いたいがための小技ですが、最善かはあやしい。

16行目で hidden 要素の値を変更していますが、 change イベントを明示的に発火させないとカスタマイザーのリアルタイムプレビューが動きません。というわけで17行目がポイント。値の変更と同時に dispatchEventchange イベントを発火させます。

手順3 カスタマイザーに複数選択チェックボックスを表示させる

functions.php にカスタマイザーのカスタマイズを記述していきます。 以下をコピペします。

JSファイルやクラスファイルのパスや、setting名、デフォルト値、選択肢などは環境に合わせて変更してください。

<?php
add_action( 'customize_controls_enqueue_scripts', 'my_customize_controls_enqueue_scripts' );
function my_customize_controls_enqueue_scripts() {
  wp_enqueue_script(
    'my-theme-customize',
    get_template_directory_uri() . '/path/to/theme-customize.js',
    array(),
    filemtime( get_template_directory() . '/path/to/theme-customize.js' ),
    true
  );
}

add_action( 'customize_register', 'my_customize_register' );
function my_customize_register( $wp_customize ) {

  // クラス定義はmy_customize_register関数内でなければならない
  include '/path/to/class-customize-multiple-checkbox-control.php';
  
  $wp_customize->add_section(
    'my-section', // セクションID 任意
    array(
      'title'       => 'セクション名',
      'description' => '説明文',
      'priority'    => 1000
    )
  );
  
  $wp_customize->add_setting(
    'my-setting', // 設定ID 任意
    array(
      'default' => [
        'twitter',
        'facebook',
        'hatebu',
        'pocket'
      ],
      'sanitize_callback' => 'my_sanitize_multiple_checkbox' // 自作のサニタイズ用関数を指定
    )
  );

  $wp_customize->add_control(
    new My_Customize_Multiple_Checkbox_Control (
      $wp_customize,
      'my-control', // コントロールID 任意
      array(
        'section'     => 'my-section',  // セクションIDを指定
        'settings'    => 'my-setting', // 設定IDを指定
        'label'       => 'ラベル名',
        'description' => '説明文',
        'choices'     => array(
          'twitter'  => 'Twitter',
          'facebook' => 'Facebook',
          'hatebu'   => 'はてなブックマーク',
          'pocket'   => 'Pocket'
        )
      )
    )
  );
}

カスタマイザーカスタマイズの基本的な説明は割愛します。

2-11行目はJavaScriptの読み込み。アクションフック customize_controls_enqueue_scripts にひっかけるのがポイント。

17行目で作ったカスタムクラスを読み込んでいます。カスタムクラスの定義は必ず my_customie_register() 内で行う必要があります。

外で読み込むと以下のように継承元のクラスが見つかりません、と怒られます。

Fatal error: Uncaught Error: Class 'WP_Customize_Control' not found in ...

26行目〜 $wp_customize->add_setting()default は配列で指定しておきます。

30行目sanitize_callback には、後述の独自関数の名前を指定しておきます。

35行目〜 $wp-customize->add_control() に自作カスタムクラスを設定します。基本的にはカラーピッカーなどと同じ要領です。

choices には選択肢を 値 => ラベル のかたちで配列指定します。

手順4 サニタイズ関数を作る

カンマ区切りの文字列を配列にして保存するためのサニタイズ関数も自作しておきます。必ず配列で保存されるようにすることでUI側での利用がシンプルになります。

// 値を配列で保存
function my_sanitize_multiple_checkbox( $values ) {
  $multi_values = !is_array( $values ) ? explode( ',', $values ) : $values;
  return !empty( $multi_values ) ? array_map( 'sanitize_text_field', $multi_values ) : array();
}

手順5 UIに表示させる

保存された値は get_theme_mod() で取り出し、あとは普通の配列として利用できます。

<?php
// UIに表示させる
$values = get_theme_mod( 'my-setting' ); // 配列形式

if ( !empty( $values )) {
  foreach ( $values as $value ) {
    echo $value;
  }
}

以上で完成です。お疲れさまでした!

コードの全貌

Gistにあげておきました。

参考文献