WordPress Custom Dropdown Navigation Menu (Submenus)

Displaying submenus in a custom WordPress theme might seem challenging because it involves working with WordPress's menu system and CSS to achieve the desired design and functionality.

WordPress's menu system is flexible but can be complex for beginners. Custom themes might require you to understand how to register menu locations, create custom walkers, and use WordPress functions like wp_nav_menu() effectively. Getting the styling just right, especially for dropdown menus, often involves writing custom CSS. Ensuring that submenus are visually appealing and responsive across various devices can be time-consuming.

Here's how I was able to display submenus in my theme:

<?php
                    wp_nav_menu(
                        array(
                            'theme_location' => 'menu-1',
                            'menu_id'        => 'primary-menu',
                            'container'     => 'ul',
                            'menu_class' => 'navbar-nav',
                            'walker' => new Custom_Walker_Nav_Menu(),
                        )
                    );

                    class Custom_Walker_Nav_Menu extends Walker_Nav_Menu
                    {

                        private $top_level = true; //made a variable to wrap submenus a tag with div

                        function start_lvl(&$output,  $depth = 0, $args = null)
                        {

                            $output .= '<ul>';
                        }

                        function start_el(&$output, $item, $depth = 0, $args = null, $id = 0)
                        {

                            if ($depth > 0) {
                            } else {
                                $output .= '<li class="nav-item dropdown';
                            }


                            // Check if $item->classes is an array before using in_array
                            if (is_array($item->classes) && in_array('current-menu-item', $item->classes)) {
                                $output .= ' active';
                            }

                            if ($depth > 0) {
                            } else {
                                $output .= '">';
                            }

                            if ($depth > 0) {
                                // We are at the top level
                                if ($this->top_level) {
                                    // Wrap all top-level items with a single <div>
                                    $output .= '<div class="dropdown-menu" aria-labelledby="navbarDropdown">';
                                    $this->top_level = false; // Mark that we wrapped the top-level items
                                }
                            }

                            if ($depth > 0) {

                                $output .= '<a href="' . esc_url($item->url) . '"';
                            } else {
                                $output .= '<a href="' . esc_url($item->url) . '"';
                            }

                            $output .= ' class="nav-link';

                            if ($depth > 0) {
                                $output .= ' dropdown-item';
                            }

                            $has_children = in_array('menu-item-has-children', $item->classes);
                            if ($has_children) {
                                $output .= ' dropdown-toggle" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">';
                                $output .= esc_html($item->title);
                                $output .= '<span class="fa fa-angle-down"></span>';
                            } else {
                                $output .= '">';
                                $output .= esc_html($item->title);
                            }

                            $output .= '</a>';
                        }
                        function end_el(&$output, $item, $depth = 0, $args = null, $id = 0)
                        {
                            if ($depth > 0) {
                            } else {
                                $output .= '</li>';
                            }
                        }
                        function end_lvl(&$output, $depth = 0, $args = null)
                        {
                            $output .= '</ul>';
                        }
                    }
                    ?>

This PHP code is used to generate and customize a WordPress navigation menu using the wp_nav_menu() function and a custom walker class (Custom_Walker_Nav_Menu). Here's a brief explanation of what the code does:

  1. wp_nav_menu(): This function is used to display a navigation menu in WordPress. It accepts an array of parameters to customize how the menu is displayed. In this code, it's used with the following parameters:

    • 'theme_location' => 'menu-1': Specifies the menu location to display.

    • 'menu_id' => 'primary-menu': Assigns an ID to the menu.

    • 'container' => 'ul': Wraps the menu items in a <ul> container.

    • 'menu_class' => 'navbar-nav': Adds a CSS class to the menu.

    • 'walker' => new Custom_Walker_Nav_Menu(): Specifies a custom walker class to control the menu's output.

  2. Custom_Walker_Nav_Menu class: This is a custom walker class that extends Walker_Nav_Menu, which allows you to customize the output of the menu items. Here's a brief overview of its key functions:

    • start_lvl(&$output, $depth = 0, $args = null): This function is called when starting a new submenu (child menu), and it adds an opening <ul> tag.

    • start_el(&$output, $item, $depth = 0, $args = null, $id = 0): This function is called for each menu item. It generates the HTML for each menu item, including wrapping it in <li> tags. It checks if the menu item has children (submenus) and adds appropriate classes and attributes.

    • end_el(&$output, $item, $depth = 0, $args = null, $id = 0): This function is called at the end of each menu item and adds a closing </li> tag.

    • end_lvl(&$output, $depth = 0, $args = null): This function is called at the end of each submenu (child menu) and adds a closing </ul> tag.

The code in start_el is responsible for wrapping top-level menu items in a <div> if they have submenus ($has_children). This allows you to wrap all top-level menu items with a single <div> while retaining the individual wrapping of submenu items.

Overall, this code combines the usage of wp_nav_menu() and a custom walker class to create a responsive navigation menu with dropdown functionality, styling, and conditional wrapping of menu items based on their depth and the presence of submenus.

While it may be challenging, displaying submenus in a custom WordPress theme can also be a valuable skill for web developers. As you gain experience and familiarity with WordPress, its menu system, and CSS, you'll find it becomes easier to create custom menu structures that match your design requirements. Additionally, there are many resources available online, including documentation, tutorials, and forums, where you can find help and guidance when facing challenges with custom theme development.