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 building a nav that has a class applied to the main LI's and the nested LI's. Example:

<ul>
<li class="className">Test</li>
<li class="className">Test
    <ul>
        <li class="ADifferentclassName">test</li>
        <li class="ADifferentclassName">test</li>
    </ul>
</li>
</ul>

I've figured out how to get classes on the UL's and all the LI's, but I can figure out how to get a different class on the nested LI's.

Here is my custom walker:

This changes the nest UL class

 class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"nav-main-sub-list\">\n";
  }

This strips out all the extraneous wordpress classes and adds the class nav-main-item to all the li's

    public function start_el( &$output, $item, $depth, $args )
    {

        $attributes  = '';

        ! empty ( $item->attr_title )
            // Avoid redundant titles
            and $item->attr_title !== $item->title
            and $attributes .= ' title="' . esc_attr( $item->attr_title ) .'"';

        ! empty ( $item->url )
            and $attributes .= ' href="' . esc_attr( $item->url ) .'"';

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

        // Since $output is called by reference we don't need to return anything.


        $output .= $indent . '<li class="nav-main-item">';

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

}

I just can't figure out how to change the class on the nested LI's. Nothing I've done has worked. Any tips?

share|improve this question

1 Answer 1

up vote 0 down vote accepted

Troubleshooting

Enabling the WP_DEBUG constant in wp-config.php exposed the following errors:

- Function Signatures

If Strict Standards is enabled, you'll see an error detailing incompatible method signatures. Though not absolutely necessary, I like to eliminate as many errors as possible. To correct this, the new Walker_Nav_Menu class's start_el() and start_lvl() methods' declarations need to match those of Walker_Nav_Menu class itself such that the new class can function as a drop-in replacement for the Walker_Nav_Menu class without throwing all sorts of parameter/argument related errors.

This:

class Wpse_145991_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_lvl( &$output, $depth ) {
        //...
    }

    public function start_el( &$output, $item, $depth, $args ) {
        //...
    }
}

should become this:

class Wpse_145991_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_lvl( &$output, $depth = 0, $args = array() ) {
        //...
    }

    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        //...
    }
}

- Notice: Undefined variable: indent

Near the bottom of the new class's start_el() method you'll see the line

$output .= $indent . '<li class="nav-main-item">';

But $indent is never defined within method. Walker classes tend to set an $indent variable for formatting HTML white-space to a count of $depth tab-characters. It's not functionally relevant, but this can be corrected by defining $indent somewhere before the aforementioned line:

$indent = str_repeat("\t", $depth);

At this point, the implementation posted in the question should be producing a menu without throwing any errors or warnings.


Specifying Different Classes for Different Depths

Theory

When a Walker_Nav_Menu is walk()ing, both the start_el() and start_lvl() methods are passed a $depth argument that can be thought of as representative of the number of "levels," "submenus," or <ul> elements between the current item and the data root (i.e. the top-level <ul> element.

Walker_Nav_Menu $depth Parameter

Implementation

By conditionally branching based on this $depth, you can assign different classes for different elements & levels. For instance, using a switch would allow you to fine-tune classes for each item and level:

class Wpse_145991_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_lvl( &$output, $depth = 0, $args = array() ) {
        $indent = str_repeat( "\t", $depth );

        // Select a CSS class for this `<ul>` based on $depth
        switch( $depth ) {
            case 0:
                // Top-level submenus get the 'nav-main-sub-list' class
                $class = 'nav-main-sub-list';
                break;
            case 1:
            case 2:
            case 3:
                // Submenus nested 1-3 levels deep get the 'nav-other-sub-list' class
                $class = 'nav-other-sub-list';
                break;
            default:
                // All other submenu `<ul>`s receive no class
                break;
        }

        // Only print out the 'class' attribute if a class has been assigned
        if( isset( $class ) )
            $output .= "\n$indent<ul class=\"$class\">\n";
        else
            $output .= "\n$indent<ul>\n";
    }

    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        $indent = str_repeat("\t", $depth);
        $attributes  = '';

        ! empty ( $item->attr_title )
        // Avoid redundant titles
        and $item->attr_title !== $item->title
        and $attributes .= ' title="' . esc_attr( $item->attr_title ) .'"';

        ! empty ( $item->url )
        and $attributes .= ' href="' . esc_attr( $item->url ) .'"';

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

        // Select a CSS class for this `<li>` based on $depth
        switch( $depth ) {
            case 0:
                // Top-level `<li>`s get the 'nav-main-item' class
                $class = 'nav-main-item';
                break;
            default:
                // All other `<li>`s receive no class
                break;
        }

        // Only print out the 'class' attribute if a class has been assigned
        if( isset( $class ) )
            $output .= $indent . '<li class="'. $class . '">';
        else
            $output .= $indent '<li>';

        $output .= apply_filters(
                'walker_nav_menu_start_el',
                $item_output,
                $item,
                $depth,
                $args
            );
    }
}
share|improve this answer
    
Thanks so much for the detailed response! I follow some of what you've done here - but I am having a hard time implementing changes successfully. For instance, the line if( $depth > 0 ) { $class = 'menu-item-first-level'; else $class = 'menu-item-other-level'; –  Steph Gill May 30 at 2:34
    
else $class = 'menu-item-other-level'; is giving me an error. I'm trying to apply the class nav-main-item to top level LI's and the class nav-main-sub-item to second level li's and nav-main-sub-list to second level UL's. It seems like $depth > 0 will apply the .nav-main-item class to all levels? I suppose i could handle this with css - but I'd like to try and figure this out. –  Steph Gill May 30 at 2:40
    
Hey sorry about that - I definitely had a mismatched bracket, there. You were right about $depth > 0 applying to the wrong items - I got my conditions backwards, sorry about that! I may have spoken too soon - I need to play with this, for a moment, and then I'll get back to you. –  boscho May 30 at 4:29
    
No problem - I appreciate your help immensely. I've been working on this for too long and not having much luck finding answers. Something about the wordpress codex is very confusing to me. –  Steph Gill May 30 at 11:59
    
The updates worked and your explanation was superb! –  Steph Gill May 30 at 16:35

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.