下面给出一份「从零开始」自定义 WordPress 菜单结构的完整示例,包含:
注册菜单位置
后台勾选「CSS 类」等选项
自定义 Walker 类,把默认的
结构完全拆成你想要的任何 HTML(示例用 Bootstrap 5 的 navbar 结构,你可以按需改)
在主题里调用
可选:把自定义 Walker 写成插件,方便换主题时复用
直接复制即可运行,代码里每一步都有中文注释。
一、注册菜单位置(functions.php 或插件主文件)
/**
* 1. 注册菜单位置
*/
add_action( 'after_setup_theme', 'my_register_menus' );
function my_register_menus() {
register_nav_menus( array(
'primary' => '顶部主导航',
'footer' => '底部链接',
) );
}
二、打开「CSS 类 / 链接关系 / 描述」等选项
/**
* 2. 让菜单项支持 CSS 类、链接关系(XFN)、描述
*/
add_filter( 'wp_nav_menu_args', 'my_nav_menu_args' );
function my_nav_menu_args( $args ) {
// 只对前台主菜单生效,后台不影响
if ( ! is_admin() && isset( $args['theme_location'] ) && $args['theme_location'] === 'primary' ) {
$args['depth'] = 3; // 支持三级菜单
$args['fallback_cb'] = false; // 没菜单时不 fallback 到 wp_page_menu
}
return $args;
}
// 打开后台「CSS 类」「描述」等面板
add_filter( 'wp_nav_menu_item_custom_fields', '__return_true' ); // 描述
add_filter( 'nav_menu_link_attributes', '__return_true' ); // 链接属性
三、自定义 Walker 类(重点)
在主题根目录新建 class-walker-primary.php 并引入,或直接写在 functions.php 里。
/**
* 3. 自定义 Walker:输出 Bootstrap 5 的 navbar
*
* 用法:
* wp_nav_menu( array(
* 'theme_location' => 'primary',
* 'container' => 'div',
* 'container_class' => 'collapse navbar-collapse',
* 'container_id' => 'navbarNav',
* 'menu_class' => 'navbar-nav ms-auto',
* 'walker' => new Walker_Primary(),
* ) );
*/
if ( ! class_exists( 'Walker_Primary' ) ) :
class Walker_Primary extends Walker_Nav_Menu {
/**
* 开始一级菜单 <ul>
*/
public function start_lvl( &$output, $depth = 0, $args = null ) {
$indent = str_repeat( "\t", $depth );
$output .= "\n$indent<ul class=\"dropdown-menu\">\n";
}
/**
* 结束一级菜单 </ul>
*/
public function end_lvl( &$output, $depth = 0, $args = null ) {
$indent = str_repeat( "\t", $depth );
$output .= "$indent</ul>\n";
}
/**
* 开始单个菜单项 <li>
*/
public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
// 合并类
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
// 判断是否有下拉
$has_children = in_array( 'menu-item-has-children', $classes, true );
if ( $has_children && $depth === 0 ) {
$classes[] = 'dropdown';
}
// 过滤并拼接类名
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
// 拼接 li id
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $class_names . '>';
// 链接属性
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
// 顶级且有子菜单时加 data-bs-toggle
if ( $has_children && $depth === 0 ) {
$atts['href'] = '#';
$atts['id'] = 'navbarDropdown' . $item->ID;
$atts['role'] = 'button';
$atts['aria-expanded'] = 'false';
$atts['data-bs-toggle']= 'dropdown';
$atts['class'] = 'nav-link dropdown-toggle';
} else {
$atts['class'] = $depth === 0 ? 'nav-link' : 'dropdown-item';
}
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
// 链接文本
$title = apply_filters( 'the_title', $item->title, $item->ID );
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args );
// 描述(可选)
$item_output = $args->before ?? '';
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after ?? '';
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
/**
* 结束单个菜单项 </li>
*/
public function end_el( &$output, $item, $depth = 0, $args = null ) {
$output .= "</li>\n";
}
}
endif;
四、在 header.php(或任意模板)调用
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="<?php echo esc_url( home_url( '/' ) ); ?>">LOGO</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<?php
wp_nav_menu( array(
'theme_location' => 'primary',
'container' => 'div',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'navbarNav',
'menu_class' => 'navbar-nav ms-auto',
'depth' => 3,
'walker' => new Walker_Primary(),
) );
?>
</div>
</nav>
五、做成插件(可选)
新建文件夹 /wp-content/plugins/my-walker
在里面创建 my-walker.php,插件头如下:
<?php
/**
* Plugin Name: My Custom Nav Walker
* Description: 自带 Bootstrap 5 的 nav walker,换主题也通用
* Version: 1.0
*/
if ( ! defined( 'ABSPATH' ) ) exit;
require_once plugin_dir_path( __FILE__ ) . 'class-walker-primary.php';
把上面的 Walker_Primary 类放到同目录的 class-walker-primary.php
后台启用插件即可,任何主题都能直接 ‘walker’ => new Walker_Primary()。
六、常见微调
把 start_lvl 里的 dropdown-menu 换成你自己的类
把 start_el 里 $atts[‘class’] 的判断逻辑改成「第 2 级加 sub-item」等
想输出「箭头 svg / 图标字体」:在 $item_output 前后拼就行
想加「当前项高亮」:判断 $item->current 或 in_array( ‘current-menu-item’, $classes )
七、一键复制清单
functions.php 里放「注册菜单」+「后台面板」代码
新建 class-walker-primary.php 并引入
header.php 里用 wp_nav_menu() 调用
刷新后台 → 外观 → 菜单 → 勾选 CSS 类 / 描述 → 拖菜单 → 前台看效果
至此,WordPress 菜单结构完全由你掌控,想怎么写 HTML 就怎么写。