Wordpress - Add 'has_children' class to parent li when modifying Walker_Nav_Menu
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 ) {
// ...
}
}
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="#">Home</a></li>
<li class="has_children"><a href="#">About</a>
<ul class="sub-menu">
<li><a href="#">Our Mission</a></li>
</ul>
</li>
<li><a href="#">Services</a></li>
<li class="has_children"><a href="#">Products</a>
<ul class="sub-menu">
<li><a href="#">Lorem Ipsum</a></li>
<li><a href="#">Lorem Ipsum</a></li>
</ul>
</li>
<li><a href="#">Contact Us</a></li>
</ul>
For more information on using WordPress' walker class, see Understanding the Walker Class.
Enjoy!
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.