Site icon Hip-Hop Website Design and Development

How to remove a sub-menu using walker hooks/filters without relying on a custom walker?

I currently built a pretty simple tool to create a mega menu. I add a checkbox to my menu items in the Appearance > Menus section followed by a dropdown that includes template files within the theme.

Here’s how my current function works (or doesn’t work):

function mega_menu_start_el( $output, $item, $depth, $args, $id = 0 ) {

$is_mega = FALSE;
$in_mega = FALSE;

if( $depth > 0 ) {

    $parent_ID = get_post_meta( $item->ID, '_menu_item_menu_item_parent', TRUE );

    if( $parent_ID ) {
        while( $parent_ID ) { //get the root parent
            $current_ID = $parent_ID; //on the late iteration, current_ID will hold the root parent
            $parent_ID = get_post_meta( $parent_ID, '_menu_item_menu_item_parent', TRUE );
        }
        $in_mega = get_post_meta( $current_ID, 'menu-item-make-mega', TRUE );
    }
}
else {
    $is_mega = get_post_meta( $item->ID, 'menu-item-make-mega', TRUE );
}

if( $in_mega ) {
    return;
}
elseif( !$is_mega ) {
    return $output;
}
else {      
    $template = get_post_meta( $item->ID, 'menu-item-mega-tpl', TRUE );

    if( file_exists( $template ) ) {

        $mega_menu = '%s<div id="mega-menu-%s" class="mega-menu mega-menu-%s">%s</div>';

        ob_start();             
        include( $template );
        $mega_menu_output = ob_get_clean();
        return sprintf( $mega_menu,
                $output,
                $item->ID,
                basename( str_replace( '.', '-', $template ) ),
                $mega_menu_output
            );
    }
}

}

Now this kind of works because if the menu item isn’t a mega menu it just returns the default generated output, if it’s in a mega menu it clears the output, and if it is a mega menu then it load the template and adds it next to the link like a sub-menu.

The problem is, since I can’t seem to find where the default sub-menu is generated, I still have the empty sub-menu markup (because clearing the output only clears the anchor tag and whatever is set in the “before” and “after” args):

<ul class="sub-menu">
    <li id="menu-item-105" class="menu-item menu-item-type-post_type menu-item-object-product menu-item-105"></li>
    <li id="menu-item-106" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-106"></li>
    <li id="menu-item-107" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-107"></li>
</ul>

So basically I’d like to hook into a level higher than this and check if I’m in a mega menu and if it is the case, then just nuke it.

I’ve been wrestling with this for a while so any tips would be super appreciated.

Thank you

Edit: Okay so I think doing this with the filters is impossible because there’s no filter for start_lvl (only start_el). This means I have to build a custom walker to achieve this, something I wanted to avoid to circumvent possible conflicts with themes that might already use a custom walker for their menu.

I was able to achieve what I wanted with a walker that extends the default Nav_Menu_Walker class. For anyone that is curious, I modified the function above so that it adds to the $output variable instead of returning anything.

I also added a private variable to hold whether we are currently in a mega menu since the walk method will traverse through the tree from the root parent and then all the way down to its children. This means I can set $this->_in_mega at the same time as $is_mega. Then, when I’m in start_lvl() and end_lvl() I call parent::start_lvl() or parent::end_lvl() if we’re not currently in the mega menu.

I’m going to leave my question open as I was hoping for a different solution (and someone could provide one) but for now, this works well enough for me.