べんりワザ・WordPressのカスタマイズの土台

ナビメニューを管理画面から編集できるようにする

ヘッダーのメニューを管理画面から編集できるようにする手順。register_nav_menusとwp_nav_menuの書き方、余計なdivを消す方法、現在ページを強調するCSS、ドロップダウンの作り方をまとめました。

「メニューの項目をお客さん自身で変えられるようにしてほしい」——この要件はregister_nav_menus()wp_nav_menu()の2つで実現できます。手順は3ステップです。

手順1:メニューの場所をテーマに登録する

function my_register_menus() {
  register_nav_menus([
    'global' => 'ヘッダーメニュー',
    'footer' => 'フッターメニュー',
  ]);
}
add_action('after_setup_theme', 'my_register_menus');

キー(global)がテンプレートから呼ぶ名前、値が管理画面に出る表示名です。

手順2:テンプレートで呼び出す

<?php
wp_nav_menu([
  'theme_location' => 'global',
  'container'      => false,                          // 囲みのdivを出さない
  'items_wrap'     => '<ul class="global-nav">%3$s</ul>',
  'depth'          => 2,
]);
?>

手順3:管理画面でメニューを作る

  1. 外観」→「メニュー」を開く
  2. 「新規メニューを作成」→名前を入れて「メニューを作成」
  3. 左から固定ページ・カテゴリ・カスタムリンクを追加してドラッグで並べ替え
  4. 「メニュー設定」の「メニューの位置」で「ヘッダーメニュー」にチェック
  5. 「メニューを保存」

引数の早見表

引数役割
theme_locationどの場所のメニューか'global'
container囲みの要素false(出さない)/ 'nav'
container_class囲みのclass'nav-wrap'
menu_classulに付くclass'global-nav'
items_wrapul部分のマークアップ'<ul class="x">%3$s</ul>'
depth何階層まで出すか1(子を出さない)/ 2
fallback_cbメニュー未設定時の代替false(何も出さない)
walker出力を細かく制御new My_Walker()

自動で付くclassを使う

WordPressは各liに状態を表すclassを付けてくれます。CSSを当てるだけで動きが作れます。

class付くとき
current-menu-item今表示しているページの項目
current-menu-parent / current-menu-ancestorその親の項目
menu-item-has-children子メニューを持つ項目
menu-item-{ID}項目ごとの固有class
.global-nav { display: flex; gap: 1.5rem; list-style: none; }
.global-nav a { text-decoration: none; padding: 0.5em 0; display: inline-block; }
.global-nav .current-menu-item > a { border-bottom: 2px solid currentColor; font-weight: 700; }

ドロップダウン(子メニュー)

管理画面でドラッグして右にずらすと子項目になります。CSSだけでドロップダウンにできます。

.global-nav .menu-item-has-children { position: relative; }
.global-nav .sub-menu {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 12em;
  list-style: none;
  background: #fff;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
  border-radius: 8px;
  padding: 0.5rem 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease;
}
.global-nav .menu-item-has-children:hover .sub-menu,
.global-nav .menu-item-has-children:focus-within .sub-menu {
  opacity: 1;
  visibility: visible;
}

:focus-withinも入れておくと、キーボード操作でも開くようになります。マウスだけを前提にしないのが親切な作りです。

スマホのハンバーガーメニュー

<button class="menu-toggle" aria-expanded="false" aria-controls="global-nav">
  <span class="sr-only">メニューを開く</span>☰
</button>
<?php wp_nav_menu([
  'theme_location' => 'global',
  'container'      => false,
  'items_wrap'     => '<ul id="global-nav" class="global-nav">%3$s</ul>',
]); ?>
const toggle = document.querySelector('.menu-toggle');
const nav = document.getElementById('global-nav');

toggle?.addEventListener('click', () => {
  const open = toggle.getAttribute('aria-expanded') === 'true';
  toggle.setAttribute('aria-expanded', String(!open));
  nav.classList.toggle('is-open');
});

aria-expandedを切り替えておくと、支援技術にも開閉が伝わります。

べんりワザCSSとJavaScriptの正しい読みこみ方(wp_enqueue)このJSファイルをテーマから読みこむ書き方はこちら

メニューのHTMLを完全に自作する

wp_nav_menu()の出力に合わせるのが面倒なときは、項目データだけ取ってきて自分で組む方法があります。

<?php
$locations = get_nav_menu_locations();
$items = !empty($locations['global']) ? wp_get_nav_menu_items($locations['global']) : [];
?>
<?php if ($items) : ?>
  <ul class="global-nav">
    <?php foreach ($items as $item) : ?>
      <?php if ((int) $item->menu_item_parent === 0) : ?>
        <li>
          <a href="<?php echo esc_url($item->url); ?>"><?php echo esc_html($item->title); ?></a>
        </li>
      <?php endif; ?>
    <?php endforeach; ?>
  </ul>
<?php endif; ?>

menu_item_parent0の項目が第1階層です。構造を完全に決められる代わりに、現在ページのclassなどは自分で付けることになります。

メニューを使わずページ一覧を出す

小さなサイトで「固定ページを自動で並べるだけ」なら、こちらのほうが手軽です。

<?php wp_list_pages(['title_li' => '', 'depth' => 1]); ?>

ただし順番や表示するページを管理画面から選べないので、納品するサイトではメニュー機能を使うほうが親切です。

テーマ開発管理画面から編集できるナビメニューテーマ作りの流れの中で学びたいときは、テーマ開発コースのこの回へ

まとめ

  1. register_nav_menus()で場所を登録し、wp_nav_menu()で呼び出す
  2. 管理画面で「メニューの位置」にチェックを入れるまでは表示されない
  3. 余計なマークアップはcontainer: falseitems_wrapで消せる
  4. current-menu-itemなどの自動classをCSSで使えば、強調やドロップダウンが作れる

メニューを管理画面から編集できる状態にしておくと、「リンクを1本足すだけ」の依頼が発生しなくなります。

よくある質問

メニューが表示されません
functions.phpでregister_nav_menus()を書いたうえで、管理画面の「外観」→「メニュー」でメニューを作り、「メニューの位置」にチェックを入れる必要があります。位置の割り当てを忘れているケースがいちばん多いです。
wp_nav_menuが出力する余計なdivやulを消したいです
'container' => falseでdivを消し、'items_wrap' => '<ul class="my-nav">%3$s</ul>'でul自体のマークアップを指定できます。liだけ欲しい場合はitems_wrapを'%3$s'にします。
現在表示しているページのメニュー項目を強調したいです
WordPressが自動でcurrent-menu-item(親はcurrent-menu-ancestor)というclassを付けるので、CSSでそのclassにスタイルを当てます。
メニューをHTMLで自由に組みたいです
wp_get_nav_menu_items()で項目の配列を取得し、foreachで自分のHTMLを組めます。より細かく制御したいときはWalker_Nav_Menuを継承する方法もあります。