Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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).

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?

share|improve this question
2  
Good day and welcome to WPSE. I must congratulate you on a epic constructed question for a first timer. Well constructed questions that are clear always receivges a lot of attention with good answers. +1 –  Pieter Goosen Apr 7 at 18:35
3  
The answer to this involves a custom Walker class, perhaps this will aid any potential answerers –  Tom J Nowell Apr 7 at 18:36
    
Thank you. I digged into php/wp only this weekend so im not this familiar. I tried a custom Walker, however the Walker only handles one single item at a time, so i can't compare it to the current item. And a filter does not accept my custom argument. Hmmm... –  Serthy Apr 7 at 20:36

2 Answers 2

up vote 2 down vote accepted

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;
}
share|improve this answer
    
Looks good to me at a first glance. Do you have problems with that code? Sidenote: You can shorten your first statement: $echo = ! isset( $args['echo'] ) ?: $args['echo']; –  kaiser Apr 8 at 23:26

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.

share|improve this answer
    
Thanks, I'll used some classes in css for my nav bars! :) –  Serthy Apr 9 at 16:48
    
If you ended up using this solution, could you please mark my answer as accepted? Thank you. –  Amanda Giles Apr 21 at 20:16

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.