Drop Down Menu Indicator

Update: Menu items now have the class “menu-item-has-children” that can be used for styling thank to this core ticket. So, the menu walker is no longer needed.

I think it’s important to give users a visual cue when menu items have a drop down menu.

One way I’ve handled this in the past is to use the Superfish jQuery plugin, which adds a class to any list items with children. This allows them to be styled differently- with a background image of a down arrow (for instance).

menu-example

However, I just saw a new theme released by Paul de Wouters called Spine. Instead of using javascript to apply the class- he uses a custom Walker_Nav_Menu so that the class is added to the markup directly.

I think this is a much better way to do it, and solves an issue I’ve seen in some themes where the menu items shift a bit when the new classes and styling are applied with javascript.

Custom Walker Code

I’ve adapted Paul’s code a little bit so it just adds a “has-children” css class to any top level “li” that has a “ul” contained within it:

class Custom_Nav_Walker extends Walker_Nav_Menu {

	function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
		$id_field = $this->db_fields['id'];
		if ( !empty( $children_elements[$element->$id_field] ) && ( depth == 0 ) ) {
			$element->classes[] = 'has-children'; // Use any classname you like
		}
		Walker_Nav_Menu::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
	}
	
}

Then, when you call the menu in the theme, make sure to include the walker:

<?php wp_nav_menu( array( 'theme_location' => 'primary', 'walker' => new Visual_Nav_Walker(), 'depth' => '2' ) ); ?>

However, for some reason the above code does not fall back to wp_page_menu properly when no menu is set. So, this is my workaround:

if ( has_nav_menu( 'primary' ) ) {
     wp_nav_menu( array( 'theme_location' => 'primary', 'walker' => new Visual_Nav_Walker(), 'depth' => '2' ) );
} else {
     wp_page_menu();
}

Menu Filter (Updated 3/20/13)

Chip Bennet posted an alternative approach using a filter on wp_nav_menu_objects to the WordPress Theme Reviewers List. This approach seems a bit more straightforward than a walker, so it’s also worth checking out.

Styling the List Item

There’s many ways you could go about styling this list item with the “has-children” class. For a new theme I’ve been working on, I already use glyphs from the Entypo icon font– and I think the “chevron-small-down” from this collection works great as a drop down menu indicator.

To use it with my menu (.main-navigation) I just needed to add this CSS:

.main-navigation li {
     float: left;
     position: relative;
}
.main-navigation li.has-children a {
     padding-right:40px;
}
.main-navigation li.has-children > a:after {
     content:'\e760';
     font-family: 'entypo';
     position: absolute;
     right:20px;
     speak: none;
}

(For more about using icon fonts, see this post)

I’ve also been thinking a “has-children” class might be a good core feature. Any thoughts?

About Devin

I am a developer based in Austin, Texas. I run a little theme shop called DevPress and help manage a WooCommerce shop with Universal Yums. Find me on twitter @devinsays.

16 Responses

  1. I’ve always used superfish for my drop-downs which ads the sub item indicator via JS, but this is much better and perfect for CSS only menus – slick!

    Thanks for the share ;)

  2. The custom Nav Walker is really useful. I’ve been using it when creating a WP theme with Bootstrap framework. And not only Bootstrap, but almost every CSS library (like Foundation in Spine theme), they require custom HTML markup for its element, e.g. nav menu.

    This is a good technique, and I think it should be the “official” way to customize the menu.

  3. It’s not work for me.

    This css work for me.

    #navwrap li a:not(:last-child):after {
    content: “\e760”;
    font-family: ‘entypo’;
    padding-left: 0.5em;
    speak: none;
    }

    #navwrap ul ul li a:not(:last-child):after {
    content:’\e762′;
    font-family: ‘entypo’;
    position: absolute;
    right:20px;
    speak: none;
    }

    Note: #navwrap is my menu id.

  4. Luke Etheridge

    Hey there,

    Is there a way to do this but instead wrap the parent link in a ‘parent’ div IF it has children, and also include a description to the parent if it has one?

Leave a Reply