このブログではしばしばアクセシビリティを話題に取り上げています。
アクセシビリティ対応を平たくいうとWEBサイトのバリアフリー化のことで、キーボードユーザー(マウスを使わずにTabキーやSpaceなどキーボードで操作する) や、音声ガイドを利用しているユーザーでも問題なく使えるようにすることを指します。
ですが、肝心の自社ブログサイト(WordPressサイト)はいつも放ったらかしで、グローバルナビゲーションをキーボード操作することさえできなかったので、重い腰をあげてキーボード対応させておきました。
PCからご覧いただいている方は試しにTabキーで移動していただくと、キーボード操作でサブメニューの開閉や検索フォームも操作できるのが分かると思います。
WordPressのグローバルナビゲーションがどう出力されているのか改めて調べ直したので、簡単にまとめておきます。
wp_nav_menu() とカスタムウォーカー
グローバルナビをカスタマイズするにあたって編集するのは、ナビゲーションを出力しているwp_nav_manu()と、そのオプションの1つであるカスタムウォーカーです。
利用しているWPテーマによって、wp_nav_menu()がどこで出力されているかは異なりますが、Cocoonテーマでは、 tmp/ navi.php の中にあります。
navi.phpは子テーマに入ってないので、カスタマイズするなら子テーマの中に作っておくことを推奨します。(親テーマを直接編集した場合、親テーマが更新されると上書きされて編集内容が消えます)
wp_nav_menu()とカスタムウォーカーの設定できる範囲
wp_nav_menu()には予めオプションが用意されており、包括的な設定を担当しています。
たとえば「各リンクの後ろにアイコンを表示させる」や「投稿ページと固定ページで出力するナビゲーションメニューを切り替える」ような全体的な変更です。
wp_nav_menu()では設定できない細かい部分、たとえば「サブメニューの深度によってクラス名を付け替える」などは、wp_nav_manu()のwalkerオプション(カスタムウォーカー)で設定していくことになります。
ちなみに、Cocoonテーマではメニューリンクの説明を表示させるのにカスタムウォーカーが利用されています。
cocoonのカスタムウォーカーは/lib/walkers.php に設定があるので、cocoonを利用している方は参考に見てみるのも良いと思います。
wp_nav_menu() の設定方法
wp_nav_menu() は$args という空の配列をオプションにもち、次のオプションを配列で指定できます。初期値と詳細は下の通りです。
<?php wp_nav_menu(
array(
'menu' => '', //管理画面でつけたメニューの名前
'menu_id' => '{メニューのスラッグ}-{連番}', //メインのulタグにつけるid名
'menu_class' => 'menu', //メインのulタグにつけるクラス名
'container' => 'div', // メニューを何のタグで囲むか div nav false が利用できる。falseなら囲まない
'container_id' => '', //containerのid名
'container_class' => 'menu-{メニューのスラッグ}-container', // containerのクラス名
'fallback_cb' => 'wp_page_menu', // メニューが存在しない場合に呼び出す関数
'before' => '', // リンクテキストの前に表示するテキスト
'after' => '', // リンクテキストの後に表示するテキスト
'link_before' => '', // リンクの前に表示するテキスト
'link_after' => '', // リンクの後に表示するテキスト
'echo' => true, // true: HTML出力 false:phpで返す
'depth' => 0, // 何階層まで表示するか。0を指定すると全階層
'walker' => '', // 適用するカスタムウォーカーを指定
'theme_location' => '', // テーマの中で使われる位置。あらかじめ register_nav_menu() で登録しておく
'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>', //出力形式。 %1$s に 'menu_id'の値 、 %2$s に 'menu_class'の値 、%3$sにメニュー項目が展開される。%3$sは省略不可
)
);
?>
メインのulタグのidやclass名の設定、それを囲むコンテナ用のタグ、どのメニューを使用するか、など全体的な設定が可能です。
仮に、下のようなナビゲーションメニューを作る場合、
<nav id="navi">
<ul id="main" class="main-menu">
<li><a>link 1</a></li>
<li><a>link 2</a>
<ul class="sub-menu">
<li><a>link 2-1</a></li>
<li><a>link 2-2</a>
<ul class="sub-menu sub-sub-menu">
<li><a>link 2-2-1</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
<?php wp_nav_menu(
array(
'menu_id' => 'main',
'menu_class' => 'main-menu',
'container' => 'nav',
'container_id' => 'navi'
)
);
?>
という設定になりますが、サブメニュー(.sub-menu)についてはwp_nav_manu() では設定できません。
サブメニューなど、より細部の設定はカスタムウォーカーの役割です。
カスタムウォーカー の設定方法
カスタムウォーカーは見たほうが早いと思うので、とりあえずWordPress Codexに例示されているサンプルをどんと載せます。(一部修正)
// 「MY_WALKER」の部分に好きな名前をつけて、wp_nav_manu() のwalker オプションで指定する
class class MY_WALKER extends Walker_Nav_Menu {
// start_lvl はサブメニューの開始タグを設定 多くの場合ulタグのこと。 lvl = level
// 例には出てこないが、終了タグは end_lvl() で指定できる
function start_lvl( &$output, $depth ) {
// 階層の深さに応じてインデント(前方に空白)する。
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // なぜ +1 かというと、depthは0からカウントするから視覚的なあれ
$classes = array(
'sub-menu', // とりあえず全部に sub-menu クラスをつける
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ), // $display_depthが奇数か偶数か判定
( $display_depth >=2 ? 'sub-sub-menu' : '' ), // $display_depth が2以上なら、sub-sub-menuクラスを付与
'menu-depth-' . $display_depth // 階層の深さをクラス名に入れてさらに分かりやすく
);
$class_names = implode( ' ', $classes );
// 組み立て
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
// ここからliタグの設定 start_el() で開始タグ。end_el() で終了タグを指定できる。el = element
function start_el( &$output, $item, $depth, $args=array(), $id=0 ) {
global $wp_query;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // さっきと一緒
// depth に応じてクラス付け替え
$depth_classes = array(
( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// passed classes
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// 組み立て
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
// ここからリンクの設定 各属性を指定していく
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
$args->before,
$attributes,
$args->link_before,
apply_filters( 'the_title', $item->title, $item->ID ),
$args->link_after,
$args->after
);
// 組み立て
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
適当に注釈入れてますが、それぞれのパーツを細かく設定して出力用のHTMLを構築していきます。
あとはカスタムウォーカーをfunction.php に書き込んで、wp_nav_menu() のwalkerオプションで指定してやれば完了です。
ちなみに自社サイトでは、サブメニューのulタグにもidをつけたかったので、start_lvlとend_lvlには何も出力させず、start_elとend_elで全部指定しています。