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.
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
);
}
}