Showing the current page’s parent’s sub-menu items from a custom nav menu in WordPress

There are plenty of tutorials and code snippets online describing how to show the active page’s children (or if on one of those children, the active page’s siblings) as a sub-menu in WordPress. Usually something like this:

<?php
$ref_post = empty($post->post_parent) ? $post->ID : $post->post_parent;
$children = wp_list_pages('title_li=&child_of='.$ref_post.'&echo=0');
if ($children) {
echo "<ul>$children</ul>";
}
?>

This is fine and dandy when your menus and your page hierarchy line up. But how do you achieve the same thing if you’re using a custom menu, from the Appearance -> Menus area in the site administration, and your page child-parent relationships are handled separately? There’s a little bit more to it. This snippet will grab the top level page’s submenu items and display them in an unordered list, useful for generating submenus in some scenarios.

<?php
$section_id = empty( $post->ancestors ) ? $post->ID : end( $post->ancestors );
$locations = get_nav_menu_locations();
$menu = wp_get_nav_menu_object( $locations[ 'primary' ] ); // 'primary' is our nav menu's name
$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'post_parent' => $section_id ) );
if( !empty( $menu_items ) ) {
echo '<ul class="section-submenu">';
foreach( $menu_items as $menu_item ) {
echo '<li><a href="' . $menu_item->url . '">' . $menu_item->title . '</a></li>';
}
echo '</ul>';
}
?>

Stick that code in a function and go make yourself a bag of microwave popcorn. You’ve earned it. Now get back here and step through this line by line.

A little shorthand since it’s Friday, if there are no ancestors use the current post, otherwise use the last ancestor.

$section_id = empty( $post->ancestors ) ? $post->ID : end( $post->ancestors );

Get the custom nav menus set in WordPress

$locations = get_nav_menu_locations();

You might need to change ‘primary’ if you are copying and pasting this, that’s just what we used in this theme

$menu = wp_get_nav_menu_object( $locations[ 'primary' ] ); // 'primary' is our nav menu's name

Get the submenu items of the section we identified earlier

$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'post_parent' => $section_id ) );

Now, if there are any menu items to list, spit them out in an unordered list.

 if( !empty( $menu_items ) ) {
echo '<ul class="section-submenu">';
foreach( $menu_items as $menu_item ) {
echo '<li><a href="' . $menu_item->url . '">' . $menu_item->title . '</a></li>';
}
echo '</ul>';
}

Now go get a clean rag and wipe down your keyboard, you got butter all over it. See you next year, folks.

16 responses to “Showing the current page’s parent’s sub-menu items from a custom nav menu in WordPress”

  1. hello, i’m trying to get this second code working, but no success. i paste the code but i see nothing. do you have any idea? thanks

  2. Thanks. Worked.

    Also would be helpful if you highlighted the specific code that you recommend using. Because sometimes developers are in a hurry and lots of text mixed with lots of code is a bit confusing.

    Cheers.

  3. This code doesn’t seem to work right to me. The children of the parent should match what you have added to your menu, shouldn’t it? The children that we see here are the ones you see on the page list screen. When I tried adding more menu items to the menu, they didn’t show up. This is problematic if you want to add links to your menu that aren’t really pages. The order of the submenu items of the custom menu is not being followed, from what I can see.

    • Correction: this code only seems to work if your child pages on the page list screen exactly match the child pages in the menu. If you add links or custom post types as child menu items, they will not show up in the menu produced by this code.

      • The pages do not have to be ordered in the page list screen exactly as in the menu itself (as in the page hierarchy doesn’t have to match the menu structure), however the code does only work for actual pages. It doesn’t list non-page items (like links or custom post types)

  4. This is great, and thank you for sharing it – but one problem is that it doesn’t include Custom Links, only pages, because of the array( ‘post_parent’ => $section_id ).

    I’ve made a couple of hacky amends below which displays all child menu items of any type, but it could be improved with some proper array filtering / searching. It works for now, though.

    $menu_items = wp_get_nav_menu_items( $menu->term_id );

    foreach( $menu_items as $menu_item ) {
    if($menu_item->object_id==$section_id){
    $object_id = $menu_item->ID;
    }
    if($menu_item->menu_item_parent==$object_id){
    echo ‘url . ‘” class=”button”>’ . $menu_item->title . ‘‘;
    }
    }

  5. Excellent… thank you very much… very usefull and easy
    Just a question… is the re a way to add a “current-menu-item” in the submenu?

    I tried width: $pageID = get_the_ID();
    ==========================================
    $section_id = empty( $post->ancestors ) ? $post->ID : end( $post->ancestors );
    $locations = get_nav_menu_locations();
    $menu = wp_get_nav_menu_object( $locations[ ‘primary’ ] ); // ‘primary’ is our nav menu’s name
    $menu_items = wp_get_nav_menu_items( $menu->term_id, array( ‘post_parent’ => $section_id ) );

    if( !empty( $menu_items ) and !is_archive()) {
    echo ”;
    foreach( $menu_items as $menu_item ) {

    $pageID = get_the_ID();

    echo ‘ID == $pageID ) {echo ‘ class=”current-menu-item”‘;}

    echo ‘>url . ‘”>’ . $menu_item->title . ‘‘;
    }
    echo ”;
    }
    ==========================================

    but this solution add the class to all submenu items… not only the current one.

    Any idea?
    Thank’s again

    Vinchoz

  6. … i think i found a solution by myself:

    ================================
    ancestors ) ? $post->ID : end( $post->ancestors );
    $locations = get_nav_menu_locations();
    $menu = wp_get_nav_menu_object( $locations[ ‘primary’ ] ); // ‘primary’ is our nav menu’s name
    $menu_items = wp_get_nav_menu_items( $menu->term_id, array( ‘post_parent’ => $section_id ) );

    if( !empty( $menu_items ) and !is_archive()) {
    echo ”;
    foreach( $menu_items as $menu_item ) {

    $pageID = get_the_ID(); // current page ID
    $menu_itemID = get_post_meta( $menu_item->ID, ‘_menu_item_object_id’, true ); // menu page ID
    $liactive = ‘inactive’; // default class if inactive

    if ( $menu_itemID == $pageID ) {
    $liactive = ‘current-menu-item’; // class if active
    }

    echo ‘url . ‘”>’ . $menu_item->title . ‘‘;
    }
    echo ”;
    }
    ?>
    =====================

    What do you think about it?

  7. Sorry my code have been cutted:


    ancestors ) ? $post->ID : end( $post->ancestors );
    $locations = get_nav_menu_locations();
    $menu = wp_get_nav_menu_object( $locations[ 'primary' ] ); // 'primary' is our nav menu's name
    $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'post_parent' => $section_id ) );

    if( !empty( $menu_items ) and !is_archive()) {
    echo '';
    foreach( $menu_items as $menu_item ) {

    $pageID = get_the_ID(); // current page ID
    $menu_itemID = get_post_meta( $menu_item->ID, '_menu_item_object_id', true ); // menu page ID
    $liactive = 'inactive'; // default class if inactive

    if ( $menu_itemID == $pageID ) {
    $liactive = 'current-menu-item'; // class if active
    }

    echo 'url . '">' . $menu_item->title . '';
    }
    echo '';
    }
    ?>

  8. Hi,

    I want this to happen with my menu, is it possible to combined your code with:

    ‘primary’,
    ‘container_class’ => ‘collapse navbar-collapse’,
    ‘container_id’ => ‘navbarNavDropdown’,
    ‘menu_class’ => ‘navbar-nav’,
    ‘fallback_cb’ => ”,
    ‘menu_id’ => ‘main-menu’,
    ‘walker’ => new rorentrep_WP_Bootstrap_Navwalker(),
    )
    );

    ?>

    I feel stupid for asking this, but is pretty new to this..!

    Thank you so much 🙂

  9. Hi my code was cut, here it is:

    wp_nav_menu(
    array(
    ‘theme_location’ => ‘primary’,
    ‘container_class’ => ‘collapse navbar-collapse’,
    ‘container_id’ => ‘navbarNavDropdown’,
    ‘menu_class’ => ‘navbar-nav’,
    ‘fallback_cb’ => ”,
    ‘menu_id’ => ‘main-menu’,
    ‘walker’ => new rorentrep_WP_Bootstrap_Navwalker(),
    )
    );

Leave a Reply

Your email address will not be published. Required fields are marked *