2026年5月31日

2026年5月31日

WordPressのナビゲーションメニューをカスタマイズする方法

はじめに

「ヘッダーナビゲーションのHTMLを自由に制御したい」「ドロップダウンメニューやハンバーガーメニューを実装したい」「メニュー項目にアイコンやバッジを追加したい」——Walker_Nav_MenuクラスをカスタマイズするとWordPressのナビゲーション出力を完全制御できます。

症状・原因

wp_nav_menu()のデフォルト出力ではCSSクラスの制御や独自HTMLの挿入が難しく、モダンなデザインに必要な構造を作れないことがあります。Walker_Nav_Menuを拡張することで出力HTMLを完全にカスタマイズできます。

解決手順

ステップ1:ナビゲーションメニューを登録する

// functions.php: ナビゲーションメニューエリアを登録
add_action( 'after_setup_theme', function() {
    register_nav_menus( [
        'primary'   => 'グローバルナビゲーション',
        'footer'    => 'フッターナビゲーション',
        'mobile'    => 'モバイルメニュー',
        'social'    => 'ソーシャルリンク',
    ] );
} );

ステップ2:wp_nav_menu()で基本ナビゲーションを出力する

// header.php: グローバルナビゲーション
wp_nav_menu( [
    'theme_location'  => 'primary',      // 登録したメニューエリアのID
    'container'       => 'nav',          // ラッパー要素(falseで無効化)
    'container_class' => 'global-nav',
    'container_id'    => 'global-nav',
    'menu_class'      => 'nav-list',
    'menu_id'         => 'nav-list',
    'echo'            => true,
    'fallback_cb'     => false,          // メニュー未設定時に何も表示しない
    'depth'           => 3,              // 表示するメニュー階層の深さ
    'items_wrap'      => '<ul id="%1$s" class="%2$s" role="list">%3$s</ul>',
] );

ステップ3:Walker_Nav_Menuでカスタム出力を実装する

// functions.php: カスタムNavWalker
class My_Nav_Walker extends Walker_Nav_Menu {

    // メニュー項目の開始タグ
    public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
        $classes   = empty( $item->classes ) ? [] : (array) $item->classes;
        $classes[] = 'nav-item';
        $classes[] = 'menu-item-' . $item->ID;

        // 現在のページのメニュー項目
        if ( in_array( 'current-menu-item', $classes, true ) ) {
            $classes[] = 'is-active';
        }

        // 子メニューがある場合
        $has_children = in_array( 'menu-item-has-children', $classes, true );
        if ( $has_children ) {
            $classes[] = 'has-dropdown';
        }

        $class_str = implode( ' ', array_filter( array_unique( $classes ) ) );

        $output .= '<li class="' . esc_attr( $class_str ) . '">';

        // アンカータグの属性
        $atts           = [];
        $atts['href']   = ! empty( $item->url ) ? $item->url : '#';
        $atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
        $atts['target'] = ! empty( $item->target ) ? $item->target : '';
        $atts['rel']    = ! empty( $item->xfn ) ? $item->xfn : '';

        // 現在ページはaria-current
        if ( in_array( 'current-menu-item', $classes, true ) ) {
            $atts['aria-current'] = 'page';
        }

        // 子メニューがある場合はaria-hasPopup
        if ( $has_children ) {
            $atts['aria-haspopup'] = 'true';
            $atts['aria-expanded'] = 'false';
        }

        $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

        $attrs_str = '';
        foreach ( $atts as $attr => $value ) {
            if ( is_scalar( $value ) && '' !== $value ) {
                $attrs_str .= ' ' . $attr . '="' . esc_attr( $value ) . '"';
            }
        }

        $title = apply_filters( 'the_title', $item->title, $item->ID );

        $output .= '<a' . $attrs_str . '>';
        $output .= '<span class="nav-label">' . esc_html( $title ) . '</span>';

        // 子メニューがある場合は展開ボタン
        if ( $has_children && 0 === $depth ) {
            $output .= '<button class="dropdown-toggle" aria-label="サブメニューを開く">'
                     . '<svg viewBox="0 0 12 8" width="12" aria-hidden="true"><path d="M1 1l5 5 5-5" stroke="currentColor" stroke-width="2" fill="none"/></svg>'
                     . '</button>';
        }

        $output .= '</a>';
    }

    // サブメニューの開始タグ
    public function start_lvl( &$output, $depth = 0, $args = null ) {
        $output .= '<ul class="dropdown-menu" role="list">';
    }

    // サブメニューの終了タグ
    public function end_lvl( &$output, $depth = 0, $args = null ) {
        $output .= '</ul>';
    }
}

// Walkerを使ってメニューを出力
wp_nav_menu( [
    'theme_location' => 'primary',
    'container'      => 'nav',
    'container_class' => 'global-nav',
    'walker'         => new My_Nav_Walker(),
] );

ステップ4:CSSでドロップダウンメニューを実装する

/* グローバルナビゲーション */
.global-nav {
    position: relative;
}

.nav-list {
    display: flex;
    align-items: center;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-item {
    position: relative;
}

.nav-item > a {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 1rem 1.25rem;
    color: #1d2327;
    text-decoration: none;
    font-weight: 500;
    font-size: 0.9rem;
    transition: color 0.2s;
    white-space: nowrap;
}

.nav-item > a:hover,
.nav-item.is-active > a {
    color: #2271b1;
}

/* ドロップダウンメニュー */
.dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    background: #fff;
    border: 1px solid #dcdcde;
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    list-style: none;
    margin: 0;
    padding: 0.5rem 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
    z-index: 100;
}

.nav-item.has-dropdown:hover > .dropdown-menu,
.nav-item.has-dropdown:focus-within > .dropdown-menu,
.nav-item.has-dropdown[data-open="true"] > .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-menu li a {
    display: block;
    padding: 0.6rem 1.25rem;
    color: #1d2327;
    text-decoration: none;
    font-size: 0.875rem;
    transition: background 0.15s;
}

.dropdown-menu li a:hover {
    background: #f0f6fc;
    color: #2271b1;
}

/* ハンバーガーボタン */
.hamburger-btn {
    display: none;
    flex-direction: column;
    gap: 5px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 8px;
}

.hamburger-btn span {
    display: block;
    width: 24px;
    height: 2px;
    background: #1d2327;
    transition: transform 0.3s, opacity 0.3s;
}

@media (max-width: 768px) {
    .hamburger-btn { display: flex; }

    .nav-list {
        display: none;
        flex-direction: column;
        position: fixed;
        top: 0;
        right: 0;
        width: 280px;
        height: 100vh;
        background: #fff;
        padding: 4rem 1.5rem 2rem;
        box-shadow: -4px 0 20px rgba(0,0,0,0.15);
        overflow-y: auto;
        z-index: 1000;
    }

    .nav-list.is-open { display: flex; }
}

ステップ5:JavaScriptでハンバーガーメニューとドロップダウンを制御する

// js/navigation.js
(function() {
    const btn    = document.querySelector('.hamburger-btn');
    const menu   = document.getElementById('nav-list');
    if (!btn || !menu) return;

    // ハンバーガーメニュー開閉
    btn.addEventListener('click', function() {
        const isOpen = menu.classList.toggle('is-open');
        btn.setAttribute('aria-expanded', isOpen);
        btn.setAttribute('aria-label', isOpen ? 'メニューを閉じる' : 'メニューを開く');
        document.body.style.overflow = isOpen ? 'hidden' : '';
    });

    // ESCキーで閉じる
    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape' && menu.classList.contains('is-open')) {
            menu.classList.remove('is-open');
            btn.setAttribute('aria-expanded', 'false');
            document.body.style.overflow = '';
            btn.focus();
        }
    });

    // ドロップダウンのキーボード操作
    document.querySelectorAll('.dropdown-toggle').forEach(function(toggle) {
        toggle.addEventListener('click', function(e) {
            e.preventDefault();
            const parent = toggle.closest('.has-dropdown');
            const isOpen = parent.getAttribute('data-open') === 'true';
            parent.setAttribute('data-open', !isOpen);
            toggle.setAttribute('aria-expanded', !isOpen);
        });
    });

    // メニュー外クリックで閉じる
    document.addEventListener('click', function(e) {
        if (!menu.contains(e.target) && !btn.contains(e.target)) {
            menu.classList.remove('is-open');
            btn.setAttribute('aria-expanded', 'false');
            document.body.style.overflow = '';
        }
    });
})();

注意事項

  • Walker_Nav_Menuを拡張する場合、start_el()内でapply_filters('nav_menu_link_attributes', ...)を必ず呼び出してください。プラグインがリンク属性を追加する際に必要なフックです。
  • ドロップダウンメニューはキーボード操作とスクリーンリーダーに対応するため、aria-haspopuparia-expandedを適切に設定してください。
  • wp_nav_menu()fallback_cbfalseに設定すると、メニューが設定されていない場合に何も出力されません。デフォルトではwp_page_menu()にフォールバックします。

まとめ

ナビゲーションメニューのカスタマイズは「register_nav_menus()でエリアを登録→wp_nav_menu()で基本出力→Walker_Nav_Menuを継承してHTML構造を完全制御→CSSでドロップダウン・アニメーションを実装→JSでハンバーガー開閉とアクセシビリティ対応」の流れで整備します。関連記事:WordPressのウィジェットエリアをカスタマイズする方法WordPressにパンくずリストを実装する方法

お気軽にご相談ください

お見積りへ お問い合わせへ