Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm working on a custom theme using wp_nav_menu(). What I want to do is add a caret to menu items that have sub-menus. For example, If my menu looks like this:

  • Menu Item 1
  • Menu Item 2
    • Menu Item 2a
    • Menu Item 2b
  • Menu Item 3

I want to be able to format it like this:

  • List item
  • Menu Item 1
  • Menu Item 2 >
    • Menu Item 2a
    • Menu Item 2b
  • Menu Item 3

Without knowing the structure of the menu. This seems like a pretty common formatting problem, so I was wondering if there is any built-in functionality to provide for this.

share|improve this question
add comment

migrated from stackoverflow.com Aug 25 '11 at 22:58

This question came from our site for professional and enthusiast programmers.

2 Answers

up vote 3 down vote accepted

I do this using jQuery (since it doesn't necessarily need to be in the TEXT (for screen readers, etc.) - just another option...:

jQuery(document).ready(function() {
  jQuery('ul#nav li').has('ul').addClass('parentul');
});

Then for that "parentul" class I put in a background image of an arrow and position it to the right > ...

share|improve this answer
    
Good point about screen readers. –  Devin Humbert Aug 26 '11 at 18:14
2  
sweet, I did this (except I used :after and content:" ▼";) –  William Kunkel Aug 26 '11 at 19:46
add comment

You can do this using a custom walker. Paste the following class at the bottom of your functions.php:

class Nfr_Menu_Walker extends Walker_Nav_Menu{

                /**
         * Traverse elements to create list from elements.
         *
         * Display one element if the element doesn't have any children otherwise,
         * display the element and its children. Will only traverse up to the max
         * depth and no ignore elements under that depth. It is possible to set the
         * max depth to include all depths, see walk() method.
         *
         * This method shouldn't be called directly, use the walk() method instead.
         *
         * @since 2.5.0
         *
         * @param object $element Data object
         * @param array $children_elements List of elements to continue traversing.
         * @param int $max_depth Max depth to traverse.
         * @param int $depth Depth of current element.
         * @param array $args
         * @param string $output Passed by reference. Used to append additional content.
         * @return null Null on failure with no changes to parameters.
         */
        function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

                if ( !$element )
                        return;

                $id_field = $this->db_fields['id'];

                //display this element
                if ( is_array( $args[0] ) )
                        $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

                //Adds the 'parent' class to the current item if it has children               
                if( ! empty( $children_elements[$element->$id_field] ) ) {
                        array_push($element->classes,'parent');
                        $element->title .= ' >';
                }

                $cb_args = array_merge( array(&$output, $element, $depth), $args);

                call_user_func_array(array(&$this, 'start_el'), $cb_args);

                $id = $element->$id_field;

                // descend only when the depth is right and there are childrens for this element
                if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {

                        foreach( $children_elements[ $id ] as $child ){

                                if ( !isset($newlevel) ) {
                                        $newlevel = true;
                                        //start the child delimiter
                                        $cb_args = array_merge( array(&$output, $depth), $args);
                                        call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
                                }
                                $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
                        }
                        unset( $children_elements[ $id ] );
                }

                if ( isset($newlevel) && $newlevel ){
                        //end the child delimiter
                        $cb_args = array_merge( array(&$output, $depth), $args);
                        call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
                }

                //end this element
                $cb_args = array_merge( array(&$output, $element, $depth), $args);
                call_user_func_array(array(&$this, 'end_el'), $cb_args);
        }
}

And then in header.php (or whever your wp_nav_menu is) do something like this:

<?php
    $walker = new Nfr_Menu_Walker(); 
    wp_nav_menu( array( 'theme_location' => 'primary', 'walker' => $walker ) ); 
?>

The key part of the walker is the following if statement:

if( ! empty( $children_elements[$element->$id_field] ) ) {
                    array_push($element->classes,'parent');
                    $element->title .= ' &gt;';
}

This checks if the item has children, and if it does it adds the 'parent' css class to it, and then changes the title from 'xxxx' to 'xxxx >'.

(Adapted from http://wordpress.org/support/topic/wp_nav_menu-add-a-parent-class)

share|improve this answer
    
This sounds like a great solution, especially for adding complex logic. However, for simplicity's sake I've decided to implement this in jQuery, especially because I already am using jQuery on my site. I would +1 but this is my first time on this stackexchange site and my rep isn't sufficient. –  William Kunkel Aug 26 '11 at 19:48
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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