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.