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 ) {
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>';

        include( $template );
        $mega_menu_output = ob_get_clean();
        return sprintf( $mega_menu,
                basename( str_replace( '.', '-', $template ) ),


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>

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.

1 Answer 1


best and easy way to hide submenu is to use css

.sub-menu {
   display: none !important;

Or you can use 'depth'=>1

   `wp_nav_menu( array( 'menu_id'=>'nav', 'theme_location'=>'header-menu' , 'depth' => 1) );

or create a function to set the 'depth'=>1 for a perticular menu. place this code in your theme function.php

  add_filter( 'wp_nav_menu_args', 'remove_my_header_dropdown', 1, 1 );
  function remove_my_header_dropdown( $args )
    //lookup theme_location ie: top-menu
    if ( isset( $args['theme_location'] ) && 'top-menu' == 
    $args['theme_location'] ) {
    $args['depth'] = 1; //levels of the hierarchy are to be included

    return $args;

` Hope that works for you

  • I don't know if I would agree that it's the best way but it's certainly easy :-P I was hoping to find a way that would avoid a CSS or JS fix as I feel the start_el hooks seem to be a correct way to go about it... I feel like I'm so close. Thank you for your input.
    – Gazillion
    Feb 21, 2018 at 18:19
  • 1
    I have update my answer and added one more option for you. hope that work.
    – Atif Aqeel
    Feb 21, 2018 at 20:51

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.