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.