WordPress自定义Walker:实现子菜单项的多列拆分
动态拆分WordPress导航子菜单为多列的解决方案
我来帮你实现基于子项数量动态分组的多列子菜单!你的需求确实没法单纯用CSS搞定,得在自定义Walker类里做PHP层面的逻辑处理,以下是完整的修改方案:
核心思路
我们会在Walker类中加入子项计数逻辑,当渲染到指定数量的子项时,自动闭合当前列的<ul>并开启新的列容器,最后用CSS让这些列并排显示。
修改后的SC_Menu_Walker类
class SC_Menu_Walker extends Walker_Nav_Menu { // 新增成员变量用于追踪子项计数 private $current_subitem_count = 0; private $total_subitems = 0; private $items_per_column = 3; // 可自定义每列最多显示的子项数 function start_el(&$output, $item, $depth=0, $args=[], $id=0) { global $wp_query; $indent = ( $depth ) ? str_repeat( "\t", $depth ) : ''; $this->curItem = $item; $class_names = $value = ''; $classes = empty( $item->classes ) ? array() : (array) $item->classes; $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) ); $class_names = ' class="'. esc_attr( $class_names ) . '"'; // 仅对二级及以上子菜单项做列拆分处理 if ($depth >= 1) { $this->current_subitem_count++; // 当达到每列上限时,闭合当前列并开启新列 if ($this->current_subitem_count > 1 && $this->current_subitem_count % $this->items_per_column == 1) { $output .= '</ul><ul class="'.$this->get_column_menu_class($depth).'">'; } } $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>'; if ($item->url && $item->url != '#') { $output .= '<a href="' . $item->url . '">'; } else { $output .= '<span>'; } $output .= $item->title; } function start_lvl(&$output, $depth = 0, $args = array()) { $sc_page_item_label = get_post_meta( $this->curItem->object_id, '_sc_page_item_label', true ); $sc_page_item_description = get_post_meta( $this->curItem->object_id, '_sc_page_item_description', true ); $sc_page_item_url = get_post_meta( $this->curItem->object_id, '_sc_page_item_url', true ); $sc_page_item_page_url = get_page_link($sc_page_item_url); $wrapperScrollStart=""; if ($depth == 1) { $wrapperScrollStart="<div class='sub-menu__inner'>"; $wrapperScrollEnd ='</div>'; } // 获取当前父菜单项的所有子项总数 $menu_id = is_object($args->menu) ? $args->menu->term_id : $args->menu; $menu_items = wp_get_nav_menu_items($menu_id, array( 'post_parent' => $this->curItem->ID, 'orderby' => 'menu_order', 'order' => 'ASC' )); $this->total_subitems = count($menu_items); $this->current_subitem_count = 0; // 替换原单一ul为多列容器,初始化第一列 $output .= '<div class="sub-menu sub"> <div class="o-custom-container"> <div class="sub-menu__wrapper_info o-sm-col-12 o-lg-col-5"><div class="o-lg-col-7"><h5>'.$sc_page_item_label.'</h5> <div class="u-font-size-medium">'.$sc_page_item_description.'</div></div> <div> <div class="c-learn-more-holder"> <div class="c-learn-more"> <a href="'.$sc_page_item_page_url.'" class="c-learn-more-link "></a> <div class="c-learn-more-title">Learn more</div> </div> </div> </div> </div>'.$wrapperScrollStart.'<div class="sub-menu__columns"><ul class="'.$this->get_column_menu_class($depth).'">'; } function end_lvl(&$output, $depth = 0, $args = array()) { $wrapperScrollEnd = ''; if ($depth == 1) { $wrapperScrollEnd ='</div>'; } // 闭合最后一个列的ul和多列容器 $output .= '</ul></div>'.$wrapperScrollEnd.'</div>'; } // 辅助方法:复用菜单类逻辑 private function get_column_menu_class($depth) { if ($depth == 1) { return "sub-menu__list sub-menu__list--scrollbar"; } return "sub-menu__list o-sm-col-12 o-lg-col-7 u-font-size-medium"; } }
配套CSS样式
添加以下CSS让列并排显示,可根据你的设计需求调整:
.sub-menu__columns { display: flex; flex-wrap: wrap; gap: 2rem; /* 列之间的间距 */ margin-top: 1rem; } .sub-menu__columns .sub-menu__list { flex: 1 1 calc(50% - 1rem); /* 两列布局,自动平分宽度 */ min-width: 280px; /* 小屏幕自动换行 */ list-style: none; padding: 0; }
关键逻辑说明
- 计数追踪:通过
$current_subitem_count和$total_subitems变量记录当前子项位置和总数。 - 动态拆分:在
start_el中,每渲染$items_per_column个子项就自动创建新列。 - 结构调整:将原单一
<ul>替换为<div class="sub-menu__columns">容器,包裹所有列的<ul>。 - 灵活适配:如果需要自动根据子项数量分N列,可修改
$items_per_column的计算方式,比如:// 自动分成两列,每列数量尽量平均 $this->items_per_column = ceil($this->total_subitems / 2);
这样当你的首个导航项有6个子项时,会自动分成两列各3个;如果是5个,会分成3个+2个的布局,完全满足动态分组的需求。
内容的提问来源于stack exchange,提问作者CritingZ
相关产品推荐
相关产品推荐

