I’m writing a customised walker class for wp_nav_menu and want to be able to specify if an li contains a submenu. So I want my markup to be:
<li class="has_children [other-wordpress-classes]">
<a class="parent-link">Some item</a>
<ul class="sub-menu">
I know how to add and remove the classes fine, I just cant find anything to tell me if the current item has children items.
Any ideas?
Thanks in advance.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
start_el() should get this information in its $args parameter, but it appears WordPress only fills this in if $args is an array, while for the custom navigation menus it is an object. This is reported in a Trac ticket. But no problem, you can fill this in yourself, if you also override the display_element() method in your custom walker (because this is the easiest place to access the child element array):
class WPSE16818_Walker extends Walker_Nav_Menu
{
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output )
{
$id_field = $this->db_fields['id'];
if ( is_object( $args[0] ) ) {
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_el( &$output, $item, $depth, $args ) {
if ( $args->has_children ) {
// ...
}
}
Method 2
Update: As of WordPress 3.7 (October 2013), CSS classes have been added to indicate child menu items and pages in theme menus — no need to use a custom walker as it’s taken care of in WordPress core.
The CSS classes are named menu-item-has-children and page_item_has_children.
For a complete solution for anybody in a hurry (credit to Jan Fabry’s previous answer), see the full implementation below.
Output the navigation in your theme’s template:
wp_nav_menu( array(
'theme_location' => 'navigation-primary',
'container' => false,
'container_class' => '',
'container_id' => '',
'menu_class' => '',
'menu_id' => '',
'walker' => new Selective_Walker(),
'depth' => 2
)
);
Then, include the following in your theme’s functions.php:
class Selective_Walker extends Walker_Nav_Menu {
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
$id_field = $this->db_fields['id'];
if ( is_object( $args[0] ) ) {
$args[0]->has_children = !empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_el( &$output, $item, $depth, $args ) {
if ( $args->has_children ) {
$item->classes[] = 'has_children';
}
parent::start_el(&$output, $item, $depth, $args);
}
}
The resulting HTML output will resemble the following:
<ul>
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Home</a></li>
<li class="has_children"><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">About</a>
<ul class="sub-menu">
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Our Mission</a></li>
</ul>
</li>
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Services</a></li>
<li class="has_children"><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Products</a>
<ul class="sub-menu">
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Lorem Ipsum</a></li>
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Lorem Ipsum</a></li>
</ul>
</li>
<li><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener">Contact Us</a></li>
</ul>
For more information on using WordPress’ walker class, see Understanding the Walker Class.
Enjoy!
Method 3
This function is exactly what you want to have. It also shows you a pretty effective way to modify nav menu items. Furthermore you can open it for more advanced (eg. child theme) functions via the item-filter:
/**
* Classes for a navigation named "Topnav" in the nav location "top".
* Shows examples on how to modify the current nav menu item
*
* @param (object) $items
* @param (object) $menu
* @param (object) $args
*/
function wpse16818_nav_menu_items( $items, $menu, $args )
{
# >>>> start editing
// examples for possible targets
$target['name'] = 'Topnav';
// The targeted menu item/s
$target['items'] = array( (int) 6 );
# <<<< stop editing
// filter for child themes: "config_nav_menu_topnav"
$target = apply_filters( 'config_nav_menu_'.strtolower( $target['name'] ), $target );
// Abort if we're not with the named menu
if ( $menu->name !== $target['name'] )
return;
foreach ( $items as $item )
{
// Check what $item contains
echo '<pre>'; print_r($item); echo '</pre>';
// First real world example:
$item->classes = 'span-4';
// Second real world example:
// Append this class if we are in one of the targeted items
if ( in_array( (int) $item->menu_order, $target['items'] ) )
$item->classes .= ' last';
}
return $items;
}
add_filter( 'wp_get_nav_menu_items', 'wpse16818_nav_menu_items', 10, 3 );
And yes, there’s – in nearly every case – no need for a custom walker.
Method 4
if you want to make drop down, you can do it with css only.
make custom nav in WP with children, WordPress automatically assigns class .sub-menu to child ul. Try this CSS
nav li {position:relative;}
.sub-menu {display:none; position:absolute; width:300px;}
nav ul li:hover ul {display:block;}
You may want to add some jQuery to spice it up a little, but this should give you a working drop down menu.
Method 5
if ( $this->has_children ) {
$item_output .= 'has_children';
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0