Skip to content

Instantly share code, notes, and snippets.

@cdsaenz
Last active April 22, 2025 18:17
Show Gist options
  • Save cdsaenz/d6d65294d79a0b71b95c55a4bbd47f7d to your computer and use it in GitHub Desktop.
Save cdsaenz/d6d65294d79a0b71b95c55a4bbd47f7d to your computer and use it in GitHub Desktop.
Wordpress Bootstrap 5 Nav Walker With Multiple Levels
<?php
/**
* CSDev - Bootstrap 5 wp_nav_menu walker
* Supports WP MultiLevel menus
* Based on https://github.com/AlexWebLab/bootstrap-5-wordpress-navbar-walker
* Requires additional CSS fixes
* CSS at https://gist.github.com/cdsaenz/d401330ba9705cfe7c18b19634c83004
* CHANGE: removed custom display_element. Just call the menu with a $depth of 3 or more.
*/
class bs5_Walker extends Walker_Nav_menu
{
private $current_item;
private $dropdown_menu_alignment_values = [
'dropdown-menu-start',
'dropdown-menu-end',
'dropdown-menu-sm-start',
'dropdown-menu-sm-end',
'dropdown-menu-md-start',
'dropdown-menu-md-end',
'dropdown-menu-lg-start',
'dropdown-menu-lg-end',
'dropdown-menu-xl-start',
'dropdown-menu-xl-end',
'dropdown-menu-xxl-start',
'dropdown-menu-xxl-end'
];
/**
* Start Level
*/
function start_lvl(&$output, $depth = 0, $args = null)
{
$dropdown_menu_class[] = '';
foreach ($this->current_item->classes as $class) {
if (in_array($class, $this->dropdown_menu_alignment_values)) {
$dropdown_menu_class[] = $class;
}
}
$indent = str_repeat("\t", $depth);
// CSDEV changed sub-menu for dropdown-submenu
$submenu = ($depth > 0) ? ' dropdown-submenu' : '';
$output .= "\n$indent<ul class=\"dropdown-menu$submenu " . esc_attr(implode(" ", $dropdown_menu_class)) . " depth_$depth\">\n";
}
/**
* Start Element
*/
function start_el(&$output, $item, $depth = 0, $args = null, $id = 0)
{
$this->current_item = $item;
$indent = ($depth) ? str_repeat("\t", $depth) : '';
$li_attributes = '';
$class_names = $value = '';
$classes = empty($item->classes) ? array() : (array) $item->classes;
$classes[] = ($args->walker->has_children) ? 'dropdown' : '';
$classes[] = 'nav-item';
$classes[] = 'nav-item-' . $item->ID;
// CSDev added dropdown-menu-child-item & at_depth classes
if ($depth && $args->walker->has_children) {
$classes[] = 'dropdown-menu-child-item dropdown-menu-end at_depth_'.$depth;
}
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args));
$class_names = ' class="' . esc_attr($class_names) . '"';
$id = apply_filters('nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args);
$id = strlen($id) ? ' id="' . esc_attr($id) . '"' : '';
$output .= $indent . '<li ' . $id . $value . $class_names . $li_attributes . '>';
$attributes = !empty($item->attr_title) ? ' title="' . esc_attr($item->attr_title) . '"' : '';
$attributes .= !empty($item->target) ? ' target="' . esc_attr($item->target) . '"' : '';
$attributes .= !empty($item->xfn) ? ' rel="' . esc_attr($item->xfn) . '"' : '';
$attributes .= !empty($item->url) ? ' href="' . esc_attr($item->url) . '"' : '';
$active_class = ($item->current || $item->current_item_ancestor || in_array("current_page_parent", $item->classes, true) || in_array("current-post-ancestor", $item->classes, true)) ? 'active' : '';
$nav_link_class = ($depth > 0) ? 'dropdown-item ' : 'nav-link ';
if ($args->walker->has_children) {
// CSDEV added data-bs-auto-close
$attributes .= ' class="' . $nav_link_class . $active_class . ' dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" data-bs-auto-close="outside" aria-expanded="false"';
}
else {
$attributes .= ' class="' . $nav_link_class . $active_class . '"';
}
$item_output = $args->before;
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
}
}
// register a new menu
register_nav_menu('main-menu', 'Main menu');
@currentcreative
Copy link

Is this something you would just add to functions.php? Does it replace or extend a default Bootstrap 5 nav walker?

I'd like to use this for my Understrap project. It calls the Bootstrap 5 nav walkers from a global template. It has an option for both the Collapse navbar and the Off-Canvas navbar.

@cdsaenz
Copy link
Author

cdsaenz commented Mar 2, 2023

Hi! I've checked Understrap docs, it's very cool. For now I'm using Bootscore, with all the woo templates, it uses both collapse & off-canvas too. In their repo it's where the discussion for this code started (based on several people's code of course). Yeah definitely it's a drop-in replacement, you change $depth to 3 or higher and it goes. I've used it with Bootscore myself, but well a revision is always a good idea.

@currentcreative
Copy link

Awesome, thanks!

@ecerda
Copy link

ecerda commented Apr 8, 2023

Hello! Thank you very much for this adjustment! Do you know if it is possible to keep the parent menu clickable?

@cdsaenz
Copy link
Author

cdsaenz commented Apr 8, 2023

Hello! Thank you very much for this adjustment! Do you know if it is possible to keep the parent menu clickable?

You mean the parent of a dropdown? I don't think so.. That's probably by (Bootstrap) design.

@MohcinBN
Copy link

Thanks for this walker :)

@leqnam
Copy link

leqnam commented Dec 12, 2024

Hi! I've checked Understrap docs, it's very cool. For now I'm using Bootscore, with all the woo templates, it uses both collapse & off-canvas too. In their repo it's where the discussion for this code started (based on several people's code of course). Yeah definitely it's a drop-in replacement, you change $depth to 3 or higher and it goes. I've used it with Bootscore myself, but well a revision is always a good idea.

Bootscore not supported muti-level menu, just 2nd level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment