0

I'm looking for a way to wrap a menu's top-level link and sub-menus into a custom div. I found this code that almost does the trick:

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

This outputs the following:

<ul>
    <li>
        <a href="#"></a>
        <div class="sub-menu-wrapper">
            <ul class="sub-menu"></ul>
        </div>
    </li>
</ul>

Though I'd need the 'a href..' to be inside the wrapper as well, like this:

<ul>
    <li>
        <div class="sub-menu-wrapper">
            <a href="#"></a>
            <ul class="sub-menu"></ul>
        </div>
    </li>
</ul>

Any suggestions? Trying to do it via the walker class and avoid jQuery's .wrap().

1

This would do it:

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
    private $_before = null;

    public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        // Backup the original "before", if any.
        if ( null === $this->_before ) {
            $this->_before = $args->before;
        }
        // Now add the wrapper div, if applicable.
        if ( 0 == $depth ) {
            $args->before = '<div class="sub-menu-wrapper">' . $this->_before;
        } else {
            $args->before = $this->_before;
        }

        // Then let the parent walker does the output generation.
        parent::start_el( $output, $item, $depth, $args, $id );
    }

    public function end_el( &$output, $item, $depth = 0, $args = array() ) {
        // Close the wrapper div, if applicable.
        if ( 0 == $depth ) {
            $output .= '</div><!-- .sub-menu-wrapper -->';
        }
        $output .= "</li>\n";
    }
}

I.e. Extend the start_el() and end_el() methods/functions instead of start_lvl() and end_lvl(). Also, the wrapper will only be added if the menu item has children (or submenu(s)).

Sample usage:

wp_nav_menu( [
    'walker' => new My_Walker_Nav_Menu(),
    // ... other args.
] );
  • I hope you don't mind I re-posted the answer as a new answer, and let me know how it goes. :) – Sally CJ Jul 27 at 17:12
  • 1
    I don't mind at all. And it's so close now! :) I'd just need the wrapper to be present even if there's no sub-menu, so just around the link in that case. – Ray Jul 27 at 19:24
  • Code updated. Let me know if that works. :) – Sally CJ Jul 28 at 1:29
  • 1
    Yes, looks like that did it. Massive thanks! – Ray Jul 28 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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