10

Is there a way I can wrap a div around wp_nav_menu's ul.sub-menu

So for example the markup would go from

<ul class="menu">

    <li><a href="/">Item 1</a></li>
    <li>
        <a href="/">Item 2</a>

        <ul class="sub-menu">

            <li><a href="/">Item 1</a></li>
            <li><a href="/">Item 1</a></li>

        </ul>
   </li>
    <li><a href="/">Item 1</a></li>
    <li><a href="/">Item 1</a></li>

</ul>

to this

<ul class="menu">

    <li><a href="/">Item 1</a></li>
    <li>
        <a href="/">Item 2</a>

        <div class="sub-menu-wrap">

            <ul class="sub-menu">

                <li><a href="/">Item 1</a></li>
                <li><a href="/">Item 1</a></li>

            </ul>

        </div>

   </li>

    <li><a href="/">Item 1</a></li>
    <li><a href="/">Item 1</a></li>

</ul>

Where "sub-menu-wrap" is the custom div.

Is this possible?

2 Answers 2

23

Use a custom walker, extend the methods start_lvl() and end_lvl.

Sample code, not tested:

class WPSE_78121_Sublevel_Walker extends Walker_Nav_Menu
{
    function start_lvl( &$output, $depth = 0, $args = array() ) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n$indent<div class='sub-menu-wrap'><ul class='sub-menu'>\n";
    }
    function end_lvl( &$output, $depth = 0, $args = array() ) {
        $indent = str_repeat("\t", $depth);
        $output .= "$indent</ul></div>\n";
    }
}

Usage in your theme:

wp_nav_menu(
    array (
        'theme_location' => 'your-theme-location-EDIT-THIS',
        'walker'         => new WPSE_78121_Sublevel_Walker
    )
);
6
  • Is it possible to make changes on a per-menu-item basis? e.g. is the menu item class available inside Walker_Nav_Menu and it's children?
    – Dan.
    Commented May 8, 2017 at 16:08
  • @Dan. Yes, it is.
    – fuxia
    Commented May 8, 2017 at 16:08
  • @fuxia, is this approach still healthy for WP 5+, or do you recommend an alternative technique to accomplish the same thing?
    – klewis
    Commented Jan 9, 2019 at 14:29
  • @klewis This should still work.
    – fuxia
    Commented Jan 9, 2019 at 14:30
  • How to do this only in first level.
    – jho1086
    Commented Oct 31, 2019 at 8:04
0

Proposed solution does not play nice with WordPress - WordPress default code and filters are ignored. I am giving here much more elegant solution, which keeps all filters like 'nav_menu_submenu_css_class' and other WP default code functioning:

<?php
/**
 * Wraps sub-menus in div element
 */
class My_Menu_Walker extends Walker_Nav_Menu {

    public function start_lvl( &$output, $depth = 0, $args = null ) {
        $output .=
        '<div class="wrapper">';
        parent::start_lvl($output, $depth, $args);
    }

    public function end_lvl( &$output, $depth = 0, $args = null ) {
        parent::end_lvl($output, $depth, $args);
        $output .= '</div>';
    }

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.