It’s very handy from a UI perspective to indicate which menu items have a sub-menu beneath them (often a small triangle or arrow is used). When using wp_nav_menu(), WordPress adds the “sub-menu” class to the <ul> tag of any sub-menus, but nothing to the parent list item; this makes them difficult to target.
We’ll start by adding the following to our functions.php file. Don’t forget to replace “themeslug” with, well, your own theme slug.
// Add CSS class to menus for submenu indicator
class Themeslug_Page_Navigation_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 ] ) ) {
$element->classes[] = 'themeslug-menu-item-parent';
}
Walker_Nav_Menu::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
}
Essentially, the above code is checking if the given menu item has children, and if so, append themeslug-menu-item-parent to the $classes array. For more information on what’s going on here (including walkers, a Core bug and other excitement), refer to the links at the end of this post.
For the above code to kick in, we need to tell menus to use it; this we’ll do with the walker argument when calling wp_nav_menu() (the following would go in a template file):
wp_nav_menu( array( 'walker' => new Themeslug_Page_Navigation_Walker ) );
Update July 9, 2012: See the comments below for the preferred method of filtering wp_nav_menu‘s arguments. Using the filter allows us to first make sure a menu is assigned, avoiding show-stopping errors if the menu is empty.
Now that we have our key class in place, we’ll use CSS and the content property to insert our triangle characters. Let’s add the following to style.css:
.themeslug-menu-item-parent > a:after {
color: #ccc;
content: 'a0 a0 25BC';
font-size: 10px;
vertical-align: 1px;
}
.sub-menu .themeslug-menu-item-parent > a:after {
content: 'a0 a0 a0 25B6';
}
The first rule adds some space and a down-pointing triangle to the anchor within our newly-identified menu item, while the second rule overrides the first with a right-pointing triangle for any parent menu items within sub-menus. We’re assuming a horizontal menu with dropdowns in this case; simply adjust the Unicode characters for your particular situation. Or replace them with arrows, floral hearts, airplanes… go nuts. Just remember that because we’re working in CSS, we need to escape the character’s code (just replace the “U+” with a backslash “”).
Have fun!
Additional information
Walker class code: http://wordpress.stackexchange.com/questions/16818/add-has-children-class-to-parent-li-when-modifying-walker-nav-menu
The Walker class in the Codex: http://codex.wordpress.org/Function_Reference/Walker_Class
wp_nav_menu() in the Codex: http://codex.wordpress.org/Function_Reference/wp_nav_menu
CSS content property: http://www.w3.org/wiki/CSS/Properties/content
CSS :after pseudo-element: http://www.w3.org/wiki/CSS3/Selectors/pseudo-elements/:after
List of Unicode characters: http://en.wikipedia.org/wiki/List_of_Unicode_characters
Jeremyclarke
April 26, 2012 at 12:49pmAwesome post and hardcore solution to what seems like a simple problem. Infinite bonus points for not using jQuery!
It would be nice if this didn’t require editing the wp_nav_menu call. Couldn’t you intercept the walker on a hook or filter somewhere? Probably more complicated than your nice solution if you can.
kwight
May 2, 2012 at 8:14amYou’re absolutely right: we could use the
wp_nav_menu_argsfilter instead of changing thewp_nav_menu()call, and it’s no more difficult (and certainly preferable if the hook is already in use in ourfunctions.php)./** * Set our new walker only if a menu is assigned, * and a child theme hasn't modified it to one level deep */ function themeslug_nav_menu_args( $args ) { if ( 1 !== $args[ 'depth' ] && has_nav_menu( 'menu_location' ) ) { $args[ 'walker' ] = new Themeslug_Page_Navigation_Walker; } return $args; } add_filter( 'wp_nav_menu_args', 'themeslug_nav_menu_args' );Eric Zentner
April 26, 2012 at 2:26pmHey Dying to use this…
When you say: wp_nav_menu() (the following would go in a template file):
where exactly do you mean in a template file? Which file? Does it matter which one? I’m using the Canvas theme from Woo, and have a Widget in the footer area which houses a Nav menu, and i want the arrows to show up next to the nav items that have a sub menu…..
any help???
thanks ,
eric
Eric Zentner
April 30, 2012 at 4:17pmNEVER MIND! Figured it out!
Sebastian Crane
October 25, 2012 at 1:28pmLife Saver. I’ve been searching for this exact type of solution and all I’ve got is jQuery hacks. +1
kwight
October 25, 2012 at 1:33pmHeheh, I’m not much for the jQuery hack approach to problem-solving either
c.bavota
November 21, 2012 at 9:37amI’d been using some fancy CSS to get this working but the walker approach is so much better. Thanks Kirk.
Kirk Wight
November 21, 2012 at 10:14amCool eh? Like @twigpress said, all that OOP stuff is pretty handy
Adding a Class to Sub-Menu Parent Items in WordPress | bavotasan.com
November 21, 2012 at 10:05am[...] out Kirk Wight’s article Adding a sub-menu indicator to parent menu items to see the solution I’m now using. Scroll down to the comments on Kirk’s article for an [...]
Collin
December 19, 2012 at 11:08amHey Kirk I have question.
How is it possible to add a seperate css style to a filter menu.
Example:
This is a filter menu:like:http://www.quality-tuning.co.uk/ (see Quick Search (Cars))
…..
I can use the css:
option:nth-child(1), option:nth-child(3) {
background: #000;}
Than every 1st and 3rd item has a background.
I want in select_item_1595 the background:orange
How must the css be?
CS
Kirk Wight
December 19, 2012 at 5:58pmSorry, I’ve no idea; I’ve never built a menu like that before. If you can get the name as an ID somehow, you could just target it directly with CSS.
Rick
January 15, 2013 at 1:23amThanks for posting this. Exactly what I needed for using this mobile menu extension: http://astuteo.com/mobilemenu