Filter wp_nav_menu()

I try to split my navigation into 3 single navigation bars (level 1, level 2 and level3+). Three because they’re separated over the site and they should only appear depending on the current page.

-0-------1--------2-------3+- level/depth
Home
 |
 |___ Lobby
 |
 |___ Projects
 |       |___ Project A
 |       |       |___ Review
 |       |       |___ Comments
 |       |       ____ Download
 |       ____ Project B
 |               |___ Review
 |               |___ Comments
 |               ____ Download
 |___ Blog
 |
 ____ About
         |___ Legal
         ____ Contact

The first navbar containing the level 1 is always visible. The second navbar (level 2) only when im currently on the corresponding parent page. The same goes for the third navbar (level 3+, plus because this navbar will also contain sub pages and subsub pages … of level 3).

Read More

In short: I want to display all the parent menus in their navbars and only the direct children of the current page.

What I tried:

function my_nav_menu( $args = array() )
{
    $echo = isset( $args['echo'] ) ? (bool)( $args['echo'] ) : true;
    $args['echo'] = false;

    add_filter( 'wp_get_nav_menu_items' , 'my_nav_menu_filter' , 666 , 3 );

    $menu = wp_nav_menu( $args );

    remove_filter( 'wp_nav_menu_objects' , 'my_nav_menu_filter' , 666 );

    if( $echo )
        echo $menu;
    else
        return $menu;
}

function my_nav_menu_filter( $items , $menu , $args )
{
    //var_dump( $args );

    $navLevel = isset( $args['navlevel'] ) ? (int)( $args['navlevel'] ) : 0;

    //echo 'navlevel = ' . $args['navlevel'] . ' | ' . $navLevel;

    if( $navLevel == 1 )
    {
        foreach( $items as $key => $item )
        {
            if( $item->menu_item_parent != 0 )
                unset( $items[$key] );
        }
    }
    else if( $navLevel == 2 )
    {
        foreach( $items as $key => $item )
        {
            if( $item->menu_item_parent != 0 )
            {
                $page = get_page( $item->menu_item_parent );

                if( $page->menu_item_parent == 0 )
                    continue;
            }

            unset( $items[$key] );
        }
    }
    else if( $navLevel == 3 )
    {
        foreach( $items as $key => $item )
        {
            if( $item->menu_item_parent != 0 )
            {
                $page = get_page( $item->menu_item_parent );

                if( $page->menu_item_parent != 0 )
                    continue;
            }

            unset( $items[$key] );
        }
    }
    else
    {
        //var_dump( $items );
    }

    return $items;
}

Calling this in my header.php:
<?php my_nav_menu( array( 'echo' => false , 'navlevel' => 1 ) ); ?>

However $args is set to the default values and my custom entry navlevel isnt shown in the filter.

How can I split my navbar as described?
How do I set my custom $args-entry?

Related posts

Leave a Reply

2 comments

  1. I think I got the answer:

    function my_nav_menu( $args = array() )
    {
        $echo = isset( $args['echo'] ) ? (bool)( $args['echo'] ) : true;
    
        $args['echo'] = false;
    
        add_filter( 'wp_nav_menu_objects' , 'my_filter_nav_menu' , 100 , 2 );
    
        $menu = wp_nav_menu( $args );
    
        remove_filter( 'wp_nav_menu_objects' , 'my_filter_nav_menu' , 100, 2 );
    
        if( $echo )
            echo $menu;
        else
            return $menu;
    }
    

    This does the trick: lets me alter the menuitems and custom args are still available.
    I accidentally hooked the filter into wp_get_nav_menu_items instead of wp_nav_menu_objects.
    I still having problems with the filtering, however these are probably some logical bugs..

    EDIT: i’ll solved my problem by combining navbar level 2 and navbar level 3+ into one and separating them with css

    heres the current php part:

    function serthy_filter_nav_menu( $items , $args )
    {
        $argArray = (array)( $args );
    
        if( isset( $argArray['toplevel'] ) )
        {
            foreach( $items as $key => $item )
            {
                if( $item->menu_item_parent != 0 )
                    unset( $items[$key] );
            }
    
            return $items;
        }
    
        global $post;
    
        $arr = array();
    
        foreach( $items as $key => $item )
        {
            $parentIDs = get_post_ancestors( $item->ID );
    
            foreach( $parentIDs as $i => $parentID )
            {
                if( $parentID == $post->ID )
                {
                    array_push( $arr , $item );
    
                    break;
                }
            }
        }
    
        return $arr;
    }
    
  2. It seems to me that you could handle this through CSS in that you could hide the lower level menu options by default, and then choose to show them if they have certain classes above them.

    On this Codex page, you can see the menu classes (and on your page itself). So for the “second level” you described, assuming the first level menu is level 1 – not 0.

    ul > li > ul.sub-menu { display: none; }  /* Hide by default */
    ul > li.current-menu-parent > ul.sub-menu { display: block; } /* Show menu */
    

    And then something similar for the next level down:

    ul > li > ul.sub-menu > li > ul.sub-menu{ display: none; }  /* Hide by default */
    ul > li > ul.sub-menu > li.current-menu-parent > ul.sub-menu { display: block; }
    

    Obviously replace “block” with “inline-block” or whatever your menus normally are set to.

    You may need to play around to find the right combination of classes, but I’ve had good luck with this method before. WP drops a ton of classes there, might as well use them.