I would like to create a walker function, or modify somehow current wp_nav_menus
so it can produce a custom output.
Let’s imagine this structure:
menu item 1
menu item 2
submenu item a
submenu item b
menu item 3
submenu item c
submenu item d
submenu item e
The easiest part is to show in one place only main menu items (main menu 1,2,3), and it can be done with wp_nav_menus, so we don’t need to code it.
The problematic part is to show in other place submenu items of currently main item…
So if user is on ‘menu item 1’ page, nothing shows.
If user is on ‘menu item 2’ page, our “new” custom menu shows:
submenu item a
submenu item b
Same menu items renders when user is on either of two above pages (menu items).
When user clicks ‘menu item 3’ and visits its target page, he sees submenu items c,d,e, same as after clicking on any of those submenu items.
If the menu has 3rd level elements (and deeper), custom menu should display all child elements of current most top menu element, beside that top element itself (which will be listed in top part of the site described in first place)
Is there a way of creating such functionality?
The idea is close to:
Display a portion/branch of menu
just it needs to be dynamic and display child elements of current menu branch/all child elements of top menu branch element if they exist.
AFAIK I can’t use get_ancestors
, because it works only with hierarchical taxonomies, and here we are not talking about menu created from post/pages hierarchical structure, but with use of menu editor.
SOLVED:
Seems I was able to create correct functions kombining few methods:
/**
* The API function, just a wrap for wp_nav_menu adding filter first and removing after
*/
function filtered_nav_menu( $args = array() ) {
$echo = isset($args['echo']) ? (bool)$args['echo'] : true;
$args['echo'] = false;
add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
$menu = wp_nav_menu( $args );
remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
if ( $echo ) echo $menu;
else return $menu;
}
/**
* The filter callback, return the filtered elements
*/
function gmfnv_filter( $items ) {
$found_top_parent_ID = false;
foreach ($items as $item) {
if ( ($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1) ) {
$found_top_parent_ID = $item->ID;
}
}
$children = submenu_get_children_ids( $found_top_parent_ID, $items );
foreach ( $items as $key => $item ) {
if ( ! in_array( $item->ID, $children ) )
unset($items[$key]);
}
return $items;
}
/**
* Helper function: return children of an element using wp_filter_object_list
*/
function 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;
}
What it does – it search for current branch top ancestor by going through each $items element and checking against:
($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1)
This conditional needs to be 2 part, because top level menu element might also be current one.
If top level $item is found, then function submenu_get_children_ids
is used to list all its child elements.
The idea
Instead of create a custom walker I thought was easier filter the items using the filter
wp_nav_menu_objects
hook.This hook is defined in
/wp-includes/nav-menu-templates.php
and whe is fired, it pass to functions hooking into it an array$sorted_menu_items
that contains all the elements of the menu being printed, if the function alter that array, adding or removing items, the resulting menu will be altered.The method
If the filter to
wp_nav_menu_objects
is applyed direclty, all menus will be filtered, so I thought was better create a function that wrapwp_nav_menu
adding the filter before callingwp_nav_menu
and remove it after: in this way only the wanted menu is filtered.The filter workflow
wp_nav_menu_objects
filter hook$itemid => $parentid
The code
The code I wrote for that, make use of 5 functions, so I create a plugin to contains all that, here the code:
How To
Create a file containing this plugin put in plugins folder and activate.
When you want filter a menu as required, instead using
wp_nav_menu( $args )
useDisclaimer
Code provided as is, no warranty, but just quickly tested on PHP 5.4, WP 3.7 with twentytherteen theme active, and no other plugins: it worked in this case.