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 Answer 1

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.
] );
4
  • I hope you don't mind I re-posted the answer as a new answer, and let me know how it goes. :)
    – Sally CJ
    Commented Jul 27, 2019 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
    Commented Jul 27, 2019 at 19:24
  • Code updated. Let me know if that works. :)
    – Sally CJ
    Commented Jul 28, 2019 at 1:29
  • 1
    Yes, looks like that did it. Massive thanks!
    – Ray
    Commented Jul 28, 2019 at 10:54

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.