How to display submenus on page based on referring top-level parent?

In your example, the ‘Key Industries’ page appears to be a different page under each of those top level menus – or at the very least, the URL is different.

This most likely allows their CMS to differentiate and use the correct submenu.

Here’s what I do when I want to achieve this:

  1. Hook into wp_nav_menu_objects and check for a custom submenu argument, which will hold the parent menu item ID
  2. Within that hook, unset any menu items that don’t have that same parent
  3. Then, when calling wp_nav_menu(), supply a submenu argument with the parent menu item ID of the submenu to display (I’ll go into how to do this below)

Hooking into wp_nav_menu_objects and unsetting non-child menu items:

Here’s the function I use for this. It will check for the submenu argument on each call to wp_nav_menu(). If submenu is set, it will proceed to remove any item which is not a child of the menu item ID that submenu refers to:

add_filter("wp_nav_menu_objects", "mytheme_submenu_limit", 10, 2);

function mytheme_submenu_limit($items, $args){

    if(empty($args->submenu)){ return $items; } // if no submenu arg is set, return immediately

    $filtered = wp_filter_object_list($items, array("ID" => $args->submenu), "and", "ID");
    $parent_id = array_pop($filtered);
    $children = mytheme_submenu_get_children_ids($parent_id, $items);

    foreach($items as $key => $item){
        if(!in_array($item->ID, $children)){
            unset($items[$key]);
        }
    }

    return $items;

}

function my_theme_submenu_get_children_ids($id, $items){
    $ids = wp_filter_object_list($items, array("menu_item_parent" => $id), "and", "ID");
    foreach($ids as $id){ $ids = array_merge($ids, submenu_get_children_ids($id, $items)); }
    return $ids;
}

Calling wp_nav_menu():

When you call wp_nav_menu(), you then need to add the custom submenu argument, eg:

wp_nav_menu(array("theme_location" => "my_menu_area", "submenu" => 240));

Of course the key here is – how do you get that submenu ID?

You can do this manually if you really want to, but a better way would be to automagically determine what this ID should be by doing some hunting through the current query.

Essentially you just want to determine what the menu item ID is of the page currently being displayed, and then:

  • if that is a top level menu item (i.e. the parent is 0), send that menu item ID to the submenu argument, or
  • if that is not a top level menu item, send it’s parent ID to the submenu argument

This will then result – as long as that page is not used more than once in the menu of course! – in the correct submenu being displayed.

Techniques for this will vary depending on the set up of your site, and, crucically, how many ‘levels’ of submenus you want to support. But it could look something like this:

global $wp_query;
$queried_object = $wp_query->get_queried_object();
$menu_query_args = array("meta_query" => array("key" => "_menu_item_object_id", "value" => $queried_object->ID));

$locations = get_nav_menu_locations();
$menu_objects = wp_get_nav_menu_items($locations["my_menu_area"], $menu_query_args);

if($menu_objects[0]->menu_item_parent == 0){ $submenu_id = $menu_objects[0]->ID; }
else{ $submenu_id = $menu_objects[0]->menu_item_parent; }

wp_nav_menu(array("theme_location" => "my_menu_area", "submenu" => $submenu_id));

I must stress this is a really simplified version; you will need to understand how it works rather than just dropping it into your site. And you will probably need to add additional logic to make it behave exactly how you want to on your site. But hopefully, this is what you need to get going!

Leave a Comment