24

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.

5 Answers 5

27

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 ) {
            // ...
        }
    }
3
  • Hello Jan, Can you help me with this question? I tried your code but i couldn't make it work. Can you give me some more sample code? Commented Feb 3, 2012 at 22:18
  • Refer to the complete implementation example further down on this page.
    – rjb
    Commented Mar 6, 2013 at 20:41
  • Thanks a lot @Jan fabry.. I was held up with my custom walker.. Atlast your snippet helped me. Commented May 5, 2014 at 10:12
7

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!

2
  • Fatal error: Call-time pass-by-reference has been removed in D:\www\wordpress\wp-content\themes\wpt_theme\functions.php on line 44 Commented Jul 14, 2014 at 19:43
  • Line#44 is parent::start_el(&$output, $item, $depth, $args); Commented Jul 14, 2014 at 19:44
2

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.

1
  • Thanks, I need the walker for now but will have a look at this for next time round!
    – patnz
    Commented May 10, 2011 at 23:50
1

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.

1
  • Thanks, its for a multilevel collapsible tree menu that I'm inserting control elements into as well, but definitely good to do as much with css as possible!
    – patnz
    Commented May 10, 2011 at 23:49
-1
if ( $this->has_children ) {
    $item_output .= 'has_children';
}
2
  • 3
    Please, explain what this code does and how it is answer the question.
    – cybmeta
    Commented Nov 13, 2015 at 18:43
  • And please post the code in more context. As is, most people who visit will have no idea where to try to paste that and will get it wrong.
    – s_ha_dum
    Commented Nov 14, 2015 at 3:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.