3

I found examples adding a class to top level items, so we can display an arrow in menu items with sub-items, but is seems terrible to cope with the already built in WordPress classes, can't display the arrow with current and css hover, it just ruins all states.

The current nav menu is like this <li><a>Text</a></li>

Is there someway to add a <span class="arrow"></span> within the parent <a></a> tags instead?

Example,

Add ⇒ <span class="arrow"></span> inside ⇒ <a/></a> tags

Thus ⇒ <li><a>Text<span class="arrow"></span></a></li> that is parent.

The current code Adds the <span></span> tags outside the <a></a> tags that is parent

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_lvl(&$output, $depth, $args) {
        $indent = str_repeat("\t", $depth);
        if('primary' == $args->theme_location && $depth ==0){
            $output .='&lt;span class="arrow">&lt;/span>';
        }
        $output .= "\n$indent<ul class=\"sub-menu\">\n";
    }
}

Original question title edited from: "add span class inside <a>"

  • as you want to edit the element, not the whole level, you should extend the function start_el instead of start_lvl. here you can add the <span> inside the <a>. – fischi Nov 15 '12 at 10:11
5

Have you tried using the before or link_before parameters in your array arguments when declaring your wp_nav_menu function?

Note: use after or link_after for the opposite effect.

Example,

wp_nav_menu(

    //normal arguments here....etc

    'before'    => '<span class="arrow"></span>',

    //or 'link_before' => '<span class="arrow"></span>',


); 

From the Codex:

$before
(string) (optional) Output text before the of the link
Default: None

   e.g. 'before'    => ''

or...

$link_before
(string) (optional) Output text before the link text
Default: None

   e.g. 'link_before'    => ''

Supporting resources:

http://codex.wordpress.org/Function_Reference/wp_nav_menu

4

This actually creates two instances where it displays a span class within the tag on the primary and secondary levels so you can add different images to your span class for designing and navigating purposes.

This helps for users and developers to show if there is a drop-down to your header menu and also makes it easier to navigate within your website.

1. Add this code below to your functions.php first.

class Nav_Walker_Nav_Menu extends Walker_Nav_Menu {
     function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        global $wp_query;
        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
        $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

        $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
        $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

        $output .= $indent . '<li' . $id . $class_names .'>';

        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;

        if ( 'primary' == $args->theme_location ) {
            $submenus = 0 == $depth || 1 == $depth ? get_posts( array( 'post_type' => 'nav_menu_item', 'numberposts' => 1, 'meta_query' => array( array( 'key' => '_menu_item_menu_item_parent', 'value' => $item->ID, 'fields' => 'ids' ) ) ) ) : false;
            $item_output .= ! empty( $submenus ) ? ( 0 == $depth ? '<span class="arrow"></span>' : '<span class="sub-arrow"></span>' ) : '';
        }
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
}

2. Add code below to your header.php where your wp_nav_menu is installed.

Explained below is the code so it installs the new custom menu in this case would be Nav_Walker_Nav_Menu.

<?php wp_nav_menu( array( 'container_class' => 'menu-header', 'theme_location' => 'primary', 'walker' => new Nav_Walker_Nav_Menu() ) ); ?>

3. Add some CSS

So it is able to show your new span arrow images within your tags in primary and secondary level.

#menu-header-menu li span.arrow { height:12px;background-image: url("images/nav-arrows.png");background-position: -8px -3px;background-repeat: no-repeat;float: right;margin: 0 0px 0 10px;text-indent: -9999px;width: 12px;}
#menu-header-menu li a:hover span.arrow{ height:12px;background-image: url("images/nav-arrows.png");background-position: -39px -3px;background-repeat: no-repeat;float: right;margin: 0 0px 0 10px;text-indent: -9999px;width: 12px;}
#menu-header-menu ul.sub-menu li span.sub-arrow { height:12px;background-image: url("images/nav-arrows.png");background-position: -8px -39px;background-repeat: no-repeat;float: right;margin: -2px 0px 0 10px;text-indent: -9999px;width: 12px;}
#menu-header-menu ul.sub-menu li a:hover span.sub-arrow{ height:12px;background-image: url("images/nav-arrows.png");background-position: -39px -39px;background-repeat: no-repeat;float: right;margin: -2px 0px 0 10px;text-indent: -9999px;width: 12px;}

hope this help! :)

0

Both answer posted by userabuser and wordpress_designer are great, but I would like to post an answer what is a combination of the two.

With this answer you can use logic to define where you don't want the link_before to be applied. As well as what you want to put before the link text.

First create a very simple class like the one below. This class is actually only responsible for checking whenever the element is not a sub menu ($depth <= 0) and for cleaning and remembering the link_before value.

if ( ! class_exists('PREFIX_Menu_Walker')) {
    class PREFIX_Menu_Walker extends Walker_Nav_Menu {
        public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
            $before = property_exists($args, 'link_before') ? $args->link_before : '';

            // Empty the `link_before` when walking over a root level element
            if ($depth <= 0 && !empty($before)) {
                $args->link_before = '';
            }

            // Continue with the default/core wordpress nav menu walker class
            parent::start_el( $output, $item, $depth, $args, $id );

            // Remember what `link_before` $args where set.
            $args->link_before = $before;
        }
    }
}

The second thing you will have to do is apply the walker argument and the link_before argument to your wp_nav_menu array, like this:

wp_nav_menu([
    'link_before' => '<span>Hello</span>',
    'walker' => new PREFIX_Menu_Walker()
]);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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