Custom Secondary Menu

Custom Menus are great for sitewide menus, but what if you only want to list a section’s subpages?

In the screenshot above, you’ll see the Plays dropdown lists three subpages. And on the right side, those same subpages are listed because we’re currently in the Plays section.

There’s a few popular approaches:

  • Use wp_list_pages() to dynamically list all subpages to the current section. The downside here is you can’t easily hide items from this list, and you have to keep the page order in sync with the custom menu’s page order (so they appear in the same order). You also can’t provide custom lables for menu items. In the above example, “Current Season” is really a page called “2011-2012 Season”, and they’ll have older seasons they do not want displayed in this menu.
  • Create a custom menu for each section. It’s a lot of work maintaining all these menus, and the end user can’t easily add a new section to the site since someone will need to create a menu for that section.

Assuming your main menu has all your site’s sections and their desired subpages, we can use wp_get_nav_menu_items() to pull only the relevant subpages.

<?php
/**
* Section Menu
* Displays the subpages of the current section
*
* @author Bill Erickson
* @link http://www.billerickson.net/custom-secondary-menu
*/
function be_section_menu() {
// Only run on pages
if( !is_page() )
return;
// If top level page, use current ID; else use highest ancestor
global $post;
$section_id = empty( $post->ancestors ) ? $post->ID : end( $post->ancestors );
// Get all the menu locations
$locations = get_nav_menu_locations();
// Find out which menu is in the 'primary' location
$menu = wp_get_nav_menu_object( $locations[ 'primary' ] );
// Grab all menu items in this menu that have a parent of the current section.
// This grabs the subpages, assuming the current section is a top level page
$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'post_parent' => $section_id ) );
// If there are menu items, build the menu
if( !empty( $menu_items ) ) {
echo '<ul class="section-submenu">';
$first = true;
foreach( $menu_items as $menu_item ) {
$classes = 'page-item';
// This adds a class to the first item so I can style it differently
if( $first )
$classes .= ' first-menu-item';
$first = false;
// This marks the current menu item
if( get_the_ID() == $menu_item->object_id )
$classes .= ' current_page_item';
echo '<li class="' . $classes . '"><a href="' . $menu_item->url . '">' . $menu_item->title . '</a></li>';
}
echo '</ul>';
}
}
add_action( 'genesis_before_loop', 'be_section_menu' );
view raw functions.php hosted with ❤ by GitHub

Bill Erickson

Bill Erickson is the co-founder and lead developer at CultivateWP, a WordPress agency focusing on high performance sites for web publishers.

About Me
Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments are closed. Continue the conversation with me on Twitter: @billerickson

Comments

  1. Pat Ramsey says

    The WordPress ‘get_xxx’ functions are so much fun to play with. It’s like a suite of DIY tools ready for you to build with.

    Great post, Bill!

  2. Tim says

    Great, thanks Bill – I haven’t delved into the get_nav_menu_ functions – so simple! Appreciated.

  3. Alan Smith says

    Bill . . . In order for this query to work, do the pages need to be child pages of a Parent page in the ‘Page Attributes’ when the page is published?

    Great post. . . I always appreciate the commented documentation in the code. I find a lot of code snippets that don’t give that detail. It is very helpful and valuable to us intermediates. Helps us learn!

    • Bill Erickson says

      Yes, they need to be child pages of a parent page, and the top level in that hierarchy needs to be in the menu.

      So if you have a page structure like About > About Our Firm > John Smith
      – John Smith needs to be a child of About Our Firm
      – About Our Firm needs to be a child of About
      – The About page needs to be a top level menu item.

      So build the actual page hierarchy in the backend using page attributes, put all your top level pages as top level menu items, and then build the custom submenus however you want.

      In my screenshot, the Plays section goes about 5 levels deep. Current Season is a custom label for 2012-2013 Season, and there’s a bunch of seasons not listed in the submenu. That’s not a problem as long as the top level (Plays) is in the menu. On all pages under it, they will inherit that custom menu.

      Hope that makes sense. It’s difficult to describe, but should make sense looking at the screenshot.

  4. Alex says

    Bill,
    Another solid article. I want to take this menu function and plug it into to two menu sections on the site. one menu would be just below the main nav on the site. And the other menu location would be just above the left sidebar.

    What would be the best way to implement that? I’m thinking I just take the code provided and modify the action hook location and the menu location to ‘secondary’ from ‘primary’. Is that right? Or is there a better way?

    • Alex says

      Well, I did manage to get the menu setup in another area of the site. w0ot! But now it’s just a matter of optimizing the code. I’ve basically included the above code twice, modifying, of course, the functionname and the action hook areas.

      • alex says

        Okay, I’ve answered my own questions. I am officially stumped. I’d like to know what’s involved to make this function for children of children.

        The top Nav shows children of a top level page. Some of these children elements also have children. I’d like to show a Children of Children nav on the left. I feel like I’m describing inception. Is this method the best way, or is there another method I should look at?

          • Alex says

            This is pretty close, Bill. Thank you! What happens now is that the children of children are pulled into the side nav menu, but the children pages are still there. If you’re on a child of a child, then only show those nav items that are 3 levels deep. I’m toying around with the snippet though. I’ll let you know if I fix it, but any tips are appreciated.