输出自定义Bootstrap结构的wordpress多级菜单

wordpress 2025年8月3日 29

「即插即用」的Bootstrap5多级菜单 Walker,特点:

支持无限级子菜单;

在后台菜单里给一级菜单项勾选CSS类has-mega即可触发mega下拉;

没有has-mega时走普通dropdown;

输出结构完全符合 Bootstrap5的 navbar-nav / dropdown-menu / row / col 规范;

自带最精简的 CSS,放主题或子主题即可。

一、把 Walker 扔进functions.php(或单独文件 require)

/**
 * Bootstrap 5 Mega/Normal Menu Walker
 */
class BS5_Mega_Walker extends Walker_Nav_Menu {

    // 开始一级菜单 <li>
    public function start_lvl( &$output, $depth = 0, $args = null ) {
        $indent = str_repeat( "\t", $depth );

        // 当前父级 item 对象
        $parent = $args->has_children ? $args->walker->items[ $args->db_id ] : null;
        $is_mega = $parent && in_array( 'has-mega', (array) $parent->classes, true );

        if ( $depth === 0 && $is_mega ) {
            // mega 容器:dropdown-menu + row
            $output .= "\n$indent<div class=\"dropdown-menu mega-menu p-3\"><div class=\"row\">\n";
        } else {
            // 普通下拉
            $output .= "\n$indent<ul class=\"dropdown-menu\">\n";
        }
    }

    // 结束一级菜单 </li>
    public function end_lvl( &$output, $depth = 0, $args = null ) {
        $indent = str_repeat( "\t", $depth );
        $parent = $args->has_children ? $args->walker->items[ $args->db_id ] : null;
        $is_mega = $parent && in_array( 'has-mega', (array) $parent->classes, true );

        if ( $depth === 0 && $is_mega ) {
            $output .= "$indent</div></div>\n";
        } else {
            $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) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        // 是否 mega 父级
        $is_mega_parent = $depth === 0 && in_array( 'has-mega', $classes, true );

        // 子级元素在 mega 里用 col 包裹
        if ( $depth === 1 && $is_mega_parent ) {
            $output .= $indent . '<div class="col-sm-6 col-lg-3 mb-3">';
        } elseif ( $depth >= 1 ) {
            // 普通子菜单 li
            $output .= $indent . '<li class="dropdown-item">';
        } else {
            // 顶层 li
            $classes[] = 'nav-item';
            $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
            $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
            $output     .= $indent . '<li' . $class_names . '>';
        }

        // 链接属性
        $atts           = [];
        $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 : '';

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

        $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 );

        // 标签
        $link_tag = ( $depth >= 1 && ! $is_mega_parent ) ? 'span' : 'a';

        $item_output  = $args->before;
        $item_output .= '<' . $link_tag . $attributes . ' class="' .
                        ( $depth === 0 ? 'nav-link' : '' ) .
                        ( $depth === 0 && $args->walker->has_children ? ' dropdown-toggle' : '' ) .
                        '" data-bs-toggle="' . ( $depth === 0 && $args->walker->has_children ? 'dropdown' : '' ) . '">';
        $item_output .= $args->link_before . $title . $args->link_after;
        $item_output .= '</' . $link_tag . '>';
        $item_output .= $args->after;

        // 子级在 mega 里的结束标签
        if ( $depth === 1 && $is_mega_parent ) {
            $item_output .= '</div>';
        }

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }

    // </li> 结束
    public function end_el( &$output, $item, $depth = 0, $args = null ) {
        $is_mega_parent = $depth === 0 && in_array( 'has-mega', (array) $item->classes, true );

        if ( $depth === 1 && $is_mega_parent ) {
            // 已在 start_el 里闭合,这里什么都不做
        } elseif ( $depth >= 1 ) {
            $output .= "</li>\n";
        } else {
            $output .= "</li>\n";
        }
    }
}

二、前台调用示例

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container">
    <a class="navbar-brand" href="<?php echo home_url(); ?>">Brand</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="mainNav">
      <?php
        wp_nav_menu( [
            'theme_location'  => 'primary',
            'container'       => false,
            'menu_class'      => 'navbar-nav me-auto mb-2 mb-lg-0',
            'fallback_cb'     => false,
            'walker'          => new BS5_Mega_Walker(),
            'depth'           => 0,
        ] );
      ?>
    </div>
  </div>
</nav>

三、最简CSS(放在主题或子主题)

/* 让 mega-menu 宽度与 container 对齐 */
@media (min-width: 992px) {
  .navbar .dropdown-menu.mega-menu {
    left: 0;
    right: 0;
    width: 100%;
    margin-top: 0;
  }
}

实现功能:

给一级菜单项加 CSS 类 has-mega → 自动变 mega;

没加就是普通 dropdown;

无限级子菜单均可正常工作。

  • No posts found in this category.
  •                            

    成功案例