WordPressの自作テーマではテーマ関数が生成するマークアップを変更するのは基本中の基本である。
今回は「カテゴリーリストをアコーディオン風に開閉できるようにしたい」というオーダーを受けてやっつけたものの没になったのでここで供養する。
2014年に書いたナビゲーションメニューの改造話と一部かぶるところがあるのだが、カスタムウォーカーによるマークアップ変更についてより具体的に書いてみた。
アコーディオンというUIについて
アコーディオンというのはボタンによってターゲット要素を開閉するUIの名称である。
こういうのはCSSだけでも作れるが、CSSだけの場合はinput要素を使わざるを得なかったり、WAI-ARIAの変更ができなかったりするので私は使ったことがない。
JavaScriptを利用すればその辺の問題は解決できるが、フレームワークを利用したUIだとマークアップそのものを利用するライブラリに合わせて変更しなければならないことが多いため、Wordpressが出力するHTMLを変更できないと対応できなかったりする。慣れてないと挫折すると思う。
今回はカテゴリーリストのHTMLをBootstrapのアコーディオンのマークアップに変更している。
wp_list_categories デフォルトの動作
WordPressのカテゴリーリストを表示する時に使うテンプレートタグといえば wp_list_categories である。
デフォルトのオプションはこのようになっていて:
$args = array(
'show_option_all' => '',
'orderby' => 'name',
'order' => 'ASC',
'style' => 'list',
'show_count' => 0,
'hide_empty' => 1,
'use_desc_for_title' => 1,
'child_of' => 0,
'feed' => '',
'feed_type' => '',
'feed_image' => '',
'exclude' => '',
'exclude_tree' => '',
'include' => '',
'hierarchical' => 1,
'title_li' => __( 'Categories' ),
'show_option_none' => __( '' ),
'number' => null,
'echo' => 1,
'depth' => 0,
'current_category' => 0,
'pad_counts' => 0,
'taxonomy' => 'category',
'walker' => null
);
wp_list_categories( $args );
デフォルト設定での表示動作はこう:
- 全カテゴリーページへのリンクなし
- リストはカテゴリー名の昇順
- 番号なし箇条書きリスト形式で表示
- 投稿数は非表示
- 投稿のあるカテゴリーのみ表示
- カテゴリーの「説明」を title 属性にセット
- 子カテゴリーの制限なし
- フィードへのリンクなし/リンク画像なし
- 表示カテゴリーの指定/除外なし
- 現在のカテゴリーを CSS クラスサフィックス ‘current-cat’ とともに表示
- カテゴリーレベルをインデント表示(ネスト表示、ツリー表示)
- リストの前に、見出しとして「Category」(日本語化ファイルで定義されていればその文字列)を表示
- SQL LIMIT なし
- リストを表示(バージョン 2.3 以降では、表示/値の取得を選べる)
- 表示する階層の深さ制限なし
- 全カテゴリー
- Walker_Category クラスでリストをレンダリング
マークアップはリスト要素の入れ子になる。
<li class="categories">カテゴリー
<ul>
<li class="cat-item cat-item-1"><a href="//localhost:3000/category/category1/">カテゴリー1</a></li>
<li class="cat-item cat-item-2"><a href="//localhost:3000/category/category2/">カテゴリー2</a></li>
</ul>
</li>
オプションでタイトルなしと子カテゴリ表示階層を指定した場合:
wp_list_categories( array('title_li' => null, 'depth'=> 2) );
タイトルが消えたぶんだけ入れ子は減るが、リスト要素での出力は変わらない。
<li class="cat-item cat-item-1"><a href="//localhost:3000/category/category-1/">カテゴリー1</a>
<ul class=“children”>
<li class=“cat-item cat-item-6"><a href=“//localhost:3000/category/category-1/category-7/“>カテゴリー7</a></li>
<li class=“cat-item cat-item-7”><a href=“//localhost:3000/category/category-1/category-8/“>カテゴリー8</a></li>
<li class=“cat-item cat-item-11”><a href=“//localhost:3000/category/category-1/category-9/“>カテゴリー9</a></li>
</ul>
</li>
<li class="cat-item cat-item-2"><a href="//localhost:3000/category/category-2/">カテゴリー2</a></li>
<li class="cat-item cat-item-3"><a href="//localhost:3000/category/category-3/">カテゴリー3</a>
<ul class="children">
<li class="cat-item cat-item-4"><a href="//localhost:3000/category/category-3/category-4/">カテゴリー4</a>
</li>
<li class="cat-item cat-item-5"><a href="//localhost:3000/category/category-3/category-4/">カテゴリー4</a>
</li>
<li class="cat-item cat-item-15"><a href="//localhost:3000/category/category-3/category-5/">カテゴリー5</a>
</li>
</ul>
</li>
WalkerオプションとWalkerクラス
前にもwp_nav_menuをWalkerクラスでカスタムする話を書いているのだが、wp_list_categoriesも同じことができる。
デフォルト動作の説明に、Walker_Category クラスでリストをレンダリング とあるように、オプションで walker
が設定できる関数にはレンダリング専用のクラスが利用されているので、マークアップをカスタマイズしたい時はこのレンダリング用のWalkerクラスを変更すればいいのだ。
Worlerクラス | 説明 |
---|---|
Walker | 全てのWalkerクラスの親 |
Walker_CategoryDropdown | カテゴリーリストをドロップダウンで生成する wp_dropdown_categories(変更不可) |
Walker_Category | カテゴリーリストを生成する wp_list_categories(変更可) |
Walker_Comment | コメントリストを生成する wp_list_comments(変更可) get_comment_pages_count(変更不可) |
Walker_Nav_Menu | ナビゲーションメニューを生成する wp_nav_menu(変更可) walk_nav_menu_tree(変更可) _wp_ajax_menu_quick_search(変更可) |
Walker_PageDropdown | ページのドロップダウンリストを生成する walk_page_dropdown_tree(変更可) |
Walker_Page | ページのリストを生成する wp_list_pages(変更可) wp_page_menu(変更可) walk_page_tree(変更可) walk_page_dropdown_tree(変更可) |
Walkerクラスのファイルは全てwp-includes
内にある。
カスタムウォーカーの作成
/walker/accordion-category.php
(ディレクトリと名前はなんでもいい)を作成して、extends
で Walker_Category
を継承したクラスを作る。
<?php
/**
* Bootstrapのアコーディオン用マークアップをする
* カテゴリー用カスタムウォーカー
* wp_list_categories の walkerオプションでセットするとマークアップが切り替わる
* 'walker' => new \sample\walker\AccordionCategory
*/
namespace sample\walker;
class AccordionCategory extends \Walker_Category {
// ここに処理を書く
}
Walkerクラスはリストのレベル変更時の前後とリスト要素の前後でそれぞれ関数を呼び出すので、その関数をカスタムクラスでオーバーライドすることでマークアップが変更できる。
オーバーライドするメソッド
関数名 | 説明 |
---|---|
start_el | リストアイテム要素の開始タグの位置で呼び出される |
end_el | リストアイテム要素の終了位置で呼び出される |
start_lvl | リストの入れ子開始時に呼び出される |
end_lvl | リストの入れ子終了時に呼び出される |
パラメーター
上記4つの関数に渡されるパラメーター。
パラメーター(変数) | 説明 |
---|---|
$output | この変数に代入した文字列がHTMLとして出力される。 |
$category $object | カテゴリーのデータオブジェクト。 |
$depth | 0 であればルートの階層で、0以上であれば子階層という判別ができる。 |
$args | 関数実行時に渡されたオプション。 |
以下のコードは$args
による分岐処理は入れてないので、必要なものがあれば追加するといい。
start_el
リストアイテム要素の開始タグの位置で呼び出される関数。
親カテゴリーは子カテゴリーがある場合とない場合でマークアップを変更。
子カテゴリーがある場合はアコーディオン化、ない場合はリンクボタンとする。end_el
関数の処理を簡略化するためにマークアップを合わせている。
子カテゴリーはUL要素の子になるのでリスト要素を使う。
/**
* Starts the element output.
*
* @since 2.1.0
* @access public
*
* @see Walker::start_el()
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $category Category data object.
* @param int $depth Optional. Depth of category in reference to parents. Default 0.
* @param array $args Optional. An array of arguments. See wp_list_categories(). Default empty array.
* @param int $id Optional. ID of the current category. Default 0.
*/
public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
$header = '';
$body = '';
$link = '';
$container_id = 'site-category';
$item_id = 'cat-' . $category->slug;
$collapse_id = 'collapse-' . $category->slug;
/** This filter is documented in wp-includes/category-template.php */
$cat_name = apply_filters(
'list_cats',
esc_attr( $category->name ),
$category
);
//子カテゴリ
$child_categories = get_term_children($category->term_id, 'category');
$child_categories_count = count($child_categories);
// 親カテゴリー
if($depth === 0) {
// 子カテゴリーがある
if ($child_categories_count > 0) {
$header .= '<div class="accordion-item">';
$header .= '<h3 class="accordion-header" id="' . $item_id . '">';
$header .= '<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . $collapse_id . '" aria-expanded="false" aria-controls="' . $collapse_id . '">';
$header .= $cat_name . '</button></h3>';
$body .= '<div id="' . $collapse_id . '" class="accordion-collapse collapse" aria-labelledby="' . $item_id . '" data-bs-parent="#' . $container_id . '">';
$body .= '<div class="accordion-body">';
} else {
// 子カテゴリーがない
$header .= '<div>';
$header .= '<div class="px-1">';
$header .= '<a class="btn btn-light p-3 d-block text-start bg-white" ';
$header .= 'href="' . esc_url( get_term_link( $category ) ) . '">';
$header .= $cat_name;
$header .= '</a>';
}
} else {
// 子カテゴリー
$link .= '<a class="d-inline-flex align-items-center rounded py-3" ';
$link .= 'href="' . esc_url( get_term_link( $category ) ) . '">';
$link .= $cat_name;
$link .= '</a>';
$body .= '<li>' . $link . '</li>';
}
$output .= $header;
$output .= $body;
}
end_el
リストアイテム要素の終了位置で呼び出される関数。
親カテゴリーの場合にDIV要素を閉じるだけ。
/**
* Ends the element output, if needed.
*
* The $args parameter holds additional values that may be used with the child class methods.
*
* @since 2.1.0
* @abstract
*
* @param string $output Used to append additional content (passed by reference).
* @param object $object The data object.
* @param int $depth Depth of the item.
* @param array $args An array of additional arguments.
*/
public function end_el( &$output, $object, $depth = 0, $args = array() ) {
// カテゴリールート
if($depth === 0) {
$output .= '</div></div>';
}
}
start_lvl
リストの入れ子開始時に呼び出される関数。
カテゴリーリストだと必ず子カテゴリーの開始時になるので、親要素になるULを追加する。
/**
* Starts the list before the elements are added.
*
* @since 2.1.0
*
* @see Walker::start_lvl()
*
* @param string $output Used to append additional content. Passed by reference.
* @param int $depth Optional. Depth of category. Used for tab indentation. Default 0.
* @param array $args Optional. An array of arguments. Will only append content if style argument
* value is 'list'. See wp_list_categories(). Default empty array.
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "$indent<ul class='list-unstyled fw-normal m-0 p-0 small'>\n";
}
end_lvl
リストの入れ子終了時に呼び出される関数。
カテゴリーリストだと必ず子カテゴリーの終了時になるので、start_lvl
関数で開始したタグの終了タグを追加する。
/**
* Ends the list of after the elements are added.
*
* The $args parameter holds additional values that may be used with the child
* class methods. This method finishes the list at the end of output of the elements.
*
* @since 2.1.0
* @abstract
*
* @param string $output Used to append additional content (passed by reference).
* @param int $depth Depth of the item.
* @param array $args An array of additional arguments.
*/
public function end_lvl( &$output, $depth = 0, $args = array() ) {
$output .= '</ul>';
}
カスタムウォーカーの利用
functions.phpでrequire
したら、
require( get_template_directory() . '/walker/accordion-category.php' );
wp_list_categories
のオプションで作ったクラスを指定する。
上記コードはアコーディオンのネストには対応していないマークアップなのでdepth
は2にする。
wp_list_categories( array( 'depth' => 2, 'walker' => new \sample\walker\AccordionCategory ) );
BootstrapのCSSとJSが読みこまれていればスタイリングもついてこうなる。