Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.
If the asker does not get an answer then they have 10 days to request a refund.
$32
Custom Walker Class wp_nav_menu()
Hi,
I found a custom Nav Walker that is used to modify the wp_nav_menu() function and display a sub-navigation.
Everything works as expected but the only issue is that it adds an additional
</li> tag at the end of the output. Its not a huge deal, but it's preventing my page from validating, so I'd like to have it removed.The sample output looks like the following:
<ul class="sub-menu">
<li><a href="#"><span>Sample item 1</span></a></li>
<li><a href="#"><span>Sample item 2</span></a>
<ul class="sub-menu">
<li><a href="#"><span>Sample Sub Item 1</span></a></li>
</ul>
</li>
<li><a href="#"><span>Sample item 3</span></a></li>
<li><a href="#"><span>Sample item 4</span></a>
</ul>
</li>
The custom nav walker is below. I mainly just want to remove the trailing
</li>, but if you see other changes that need to be made please feel free. (I'm fairly new to custom nav walkers so don't know a ton about the proper syntax, etc).Thanks a lot for your help.
Custom nav walker:
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li' . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a>';
$item_output .= $args->after;
}
//}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
$output .= "</li>\n";
}
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
// If the sub-menu is empty, strip the opening tag, else closes it
if (substr($output, -22)=="<ul class=\"sub-menu\">\n") {
$output = substr($output, 0, strlen($output)-23);
} else {
$output .= "$indent</ul>\n";
}
}
}
This question has been answered.
WP Answers | 01/26/11 at 1:59pm
Edit
Previous versions of this question:
01/27/11 at 11:04am
| 01/27/11 at 11:07am
The experts have suggested, on average, a prize of $32 for this question.
(6) Possible Answers Submitted...
See a chronological view of answers?
Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.
-

Last edited:
01/26/11
2:05pmChris Lee says:Can we see the markup where you call the wp_nav_menu() function?
Previous versions of this answer: 01/26/11 at 2:05pm
- 01/26/11 2:11pm
WP Answers says:Sure thing. Here you go:
<?php
if (function_exists('wp_nav_menu')) {
$menu_args = array('walker' => new sub_nav_walker(),);
echo '<div id="sub_nav">';
wp_nav_menu($menu_args);
echo '</div><!-- end sub_nav -->';
};?>
- 01/26/11 2:11pm
-

Last edited:
01/26/11
2:10pmDan | gteh says:The issue is definitely with this function
// Closes only the opened li
if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
$output .= "</li>\n";
}
Try replacing the entire code with this:
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li' . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a></li>';
$item_output .= $args->after;
}
//}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
$output .= "";
}
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
// If the sub-menu is empty, strip the opening tag, else closes it
if (substr($output, -22)=="<ul class=\"sub-menu\">\n") {
$output = substr($output, 0, strlen($output)-23);
} else {
$output .= "$indent</ul>\n";
}
}
}
main change to:
$item_output .= '</span></a>';
added the closing </li> to that spot and removed it from the end_lvl function.
back up your code first though.Previous versions of this answer: 01/26/11 at 2:10pm
- 01/26/11 2:18pm
WP Answers says:Thank you for this.
This fixed the trailing 'li' tag, but the 3rd level sub-navs gets a little jacked up. Using the example from my original post, you will see what I mean. It essentially closes out the 'li' before nesting the 3rd level nav:
<ul class="sub-menu">
<li><a href="#"><span>Sample item 1</span></a></li>
<li><a href="#"><span>Sample item 2</span></a></li>
<ul class="sub-menu">
<li><a href="#"><span>Sample Sub Item 1</span></a></li>
</ul>
<li><a href="#"><span>Sample item 3</span></a></li>
<li><a href="#"><span>Sample item 4</span></a>
</ul>
- 01/26/11 2:23pm
Dan | gteh says:Your original code is putting the last </li> after the closing </ul> leaving the last list item without a closing tag completely.
Here's an updated version. Try this one. If this doesn't work I am unsure what else could be causing it.
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li' . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a></li>';
$item_output .= $args->after;
}
//}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
$output .= "</li>\n";
}
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
// If the sub-menu is empty, strip the opening tag, else closes it
if (substr($output, -22)=="<ul class=\"sub-menu\">\n") {
$output = substr($output, 0, strlen($output)-23);
} else {
$output .= "$indent</ul>\n";
}
}
}
- 01/26/11 2:31pm
WP Answers says:Thank you again.
The new code is still incorrect.
Its adding two closing 'li' tags to each item, and also has the 3rd level nesting issue, as well as the original issue with the trailing 'li'
If you, or anyone else, has another walker that you've used in the past, I'm certainly open to using that. I'm just looking for a walker that displays all of the sub-pages of a particular section, in nested lists, without listing the parent item.
- 01/26/11 2:44pm
Dan | gteh says:Here is one I've used in the past. Had to dig it out from a client's site.
class samplesite_walker extends Walker_Nav_Menu {
function start_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
if ($depth == 0)
$output .= "\n$indent<div class=\"drop\"><ul class=\"sub-menu\">\n";
else
$output .= "\n$indent<ul class=\"sub-menu\">\n";
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
if ($depth == 0)
$output .= "$indent</ul></div>\n";
else
$output .= "$indent</ul>\n";
}
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $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 .' class="'.strtolower(str_replace(' ','-',preg_replace('/[^a-zA-Z0-9\s] /', '', $item->title))).'">';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
- 01/26/11 3:00pm
WP Answers says:Thank you for the custom walker, but this one is calling in the entire navigation, rather then just the sub pages of the particular section.
- 01/26/11 3:10pm
Dan | gteh says:Ok. I'm all out :) Good luck though.
- 01/26/11 3:41pm
WP Answers says:Thanks brother. I really do appreciate you trying. Have a great day.
- 01/26/11 2:18pm
-

Last edited:
01/26/11
2:52pmSébastien | French WordpressDesigner says:the code must be
<?php
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (
strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li' . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a><li>';
$item_output .= $args->after;
}
//}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
$output .= "\n";
}
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
// If the sub-menu is empty, strip the opening tag, else closes it
if (substr($output, -22)=="<ul class=\"sub-menu\">\n") {
$output = substr($output, 0, strlen($output)-23);
} else {
$output .= "$indent</ul>\n";
}
}
}
?>
edit : no, yhat's not good with the third level
Previous versions of this answer: 01/26/11 at 2:52pm
- 01/26/11 2:58pm
WP Answers says:Thank you for this. This is very close but has the same issue as one of the solutions above.
When a nav item has a 3rd level sub-nav, the 'li' is closing before the nested 'ul', rather than wrapping the nested 'ul' and closing after that. See the sample below for an example. ('Sample Item 2 should not insert the closing 'li' until after the nested sub-nav below it)
<ul class="sub-menu">
<li><a href="#"><span>Sample item 1</span></a></li>
<li><a href="#"><span>Sample item 2</span></a></li>
<ul class="sub-menu">
<li><a href="#"><span>Sample Sub Item 1</span></a></li>
</ul>
<li><a href="#"><span>Sample item 3</span></a></li>
<li><a href="#"><span>Sample item 4</span></a>
</ul>
- 01/26/11 3:10pm
Sébastien | French WordpressDesigner says:the code must be simply that :
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li' . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a>';
$item_output .= $args->after;
}
//}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
}
- 01/26/11 3:21pm
WP Answers says:Hi,
This fixes the 3rd level issue, but the trailing 'li' gets added again, and it also adds a bunch of empty tags after the trailing 'li'
Sorry I feel like I'm being in pain in the arse. Don't mean to be though :)
End of the output looks like the following:
</li>
<ul class="sub-menu">
</li>
</li>
</li>
</li>
</ul>
</li>
</li>
</li>
- 01/27/11 5:30am
Sébastien | French WordpressDesigner says: - 01/27/11 11:08am
WP Answers says:Hi Mate,
Your last comment didn't get posted. Can you please re-post? Prize is now $32. Cheers. - 01/27/11 1:58pm
Sébastien | French WordpressDesigner says:This code works :-)
class sub_nav_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
#current_page_item
// Checks if the current element is in the current selection
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {//$item->menu_item_parent > id du item parent direct
$this->found_parents[] = $item->ID;
if($item->menu_item_parent!=$parent_item_id){
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $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 .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a>';
$item_output .= $args->after;
}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
$new = str_replace(array("\r\n", "\n", "\r", "\t"),'', $output);
if(ereg('<ul class="sub-menu"></ul>',$new)){
$output = str_replace('<ul class="sub-menu"></ul>','', $new);
}
}
}
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if (strpos($class_names, 'current-menu-item')
|| strpos($class_names, 'current-menu-parent')
|| strpos($class_names, 'current-menu-ancestor')
|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
$output .= "</li>\n";
}
}
}
- 01/26/11 2:58pm
-

Last edited:
01/26/11
3:12pmGabriel Reguly says:Hi,
Why are you using a custom walker?
The default walker surely can do the sub levels.
Sorry if I miss something here.
Kind Regards,
Gabriel Reguly
- 01/26/11 3:17pm
Gabriel Reguly says:Sorry, just read the other answers.
You are looking for only the sub pages of a particular section.
This surely can be done. Let me try. - 01/26/11 3:17pm
WP Answers says:Hi,
I'm totally open to using the default walker if it can achieve what I'm after.
I'm simply looking to create a sub-navigation that lists only the child-pages of the parent section you are currently in. (unlimited levels deep + without displaying parent page).
Please let me know if this is possible by default. - 01/26/11 4:13pm
Gabriel Reguly says:I don't think it is possible by default.
First one needs to find all the sub items of the current item ( I don't know how hard that could be )
and then use the following code:
function walk_nav_menu_tree( $items, $depth, $r ) {
$walker = ( empty($r->walker) ) ? new Walker_Nav_Menu : $r->walker;
$args = array( $items, $depth, $r );
return call_user_func_array( array(&$walker, 'walk'), $args );
}
$items is an array containing the found sub items
$depth should be 0
$r is the original $args from wp_nav_menu()
Hope that helps you.
Need to go, maybe tomorrow I can help you properly.
Regards,
Gabriel
- 01/26/11 3:17pm
-

Last edited:
01/26/11
4:07pmJohn Cotton says:Perhaps I'm being a bit thick here, but if you only want sub-level menu items, why don't you just test for those with the $depth value?
So you've have code like this:
// Checks if the current element is in the current selection
if ($depth > 0) {
$output .= $indent . '<li' . $class_names .'>';
//etc
}
and this:
function end_el(&$output, $item, $depth) {
// Closes only the opened li
if ( $depth > 0 ) {
$output .= "</li>\n";
}
}
- 01/26/11 4:22pm
WP Answers says:Hi John,
Thanks for this solution.
The nav is displaying correctly, however it is displaying "2 empty subnavs". One gets displayed before, and the other gets displayed after. Any ideas around this?
Here's a sample of what's being outputted:
<ul class="sub-menu">
<li></li>
<li></li>
<li></li>
</ul>
<ul class="sub-menu">
<li><a href="#"><span>Sample item 1</span></a></li>
<li><a href="#"><span>Sample item 2</span></a>
<ul class="sub-menu">
<li><a href="#"><span>Sample Sub Item 1</span></a></li>
</ul></li>
<li><a href="#"><span>Sample item 3</span></a></li>
<li><a href="#"><span>Sample item 4</span></a>
</ul>
<ul class="sub-menu">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
- 01/26/11 5:30pm
John Cotton says:Are you able to show the output of the menu using the standard walker? That might give a clue as to what's wrong.....
- 01/26/11 6:00pm
John Cotton says:As a test you could try changing this line
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
to this
$item_output .= $args->link_before . $item->title . $args->link_after;
- 01/26/11 6:25pm
WP Answers says:Hi John,
When I make the $item_output change the output is the same.
I only have one other function that affects the default wp_nav_menu() output. I tried removing it for testing purposes, but it had no affect on the output, other than wrapping everything in one more 'ul'. The function simply strips the default wrapper 'ul' added by the wp_nav_menu() output. I need to run but i'll be back online in approx 2 hours. I've listed the other function below. Thanks a lot for your help so far.
If you or anyone needs more prize money to help me come to a resolution, just let me know and we'll work something out.
// remove wp_nav_menu ul container
function my_nav_unlister($menu){
return preg_replace( array( '#^<ul[^>]*>#', '#</ul>$#' ), '', $menu );
}
add_filter( 'wp_nav_menu', 'my_nav_unlister' );
- 01/26/11 6:29pm
John Cotton says:Without knowing what's in your menu it's difficult to second guess.
if you could show us the output of
<?php
if (function_exists('wp_nav_menu')) {
$menu_args = array();
echo '<div id="sub_nav">';
wp_nav_menu($menu_args);
echo '</div><!-- end sub_nav -->';
};?>
that would help. I shall have packed up the day in 2 hours, but I'll have a look at your default output in the morning.
JC
- 01/26/11 8:58pm
WP Answers says:Sure thing John. The default output is below. I've changed over the links and titles since its a dev. site and I don't want to list the actual links to the public. Please keep in mind the purpose of this custom walker is to display *only* all of the child pages of the particular parent section you're in, without displaying the parent page itself in the list.
Thanks again for your help.
Cheers.
<div id="sub_nav"><li><a href="http://www.website.com/">Features</a>
<ul class="sub-menu">
<li><a href="http://www.website.com/features/">feature 1 sub</a></li>
<li><a href="http://www.website.com/features/">feature 2 sub</a></li>
<li><a href="http://www.website.com/features/">feature 3 sub</a></li>
</ul>
</li>
<li><a href="http://www.website.com/pages/">Pages</a>
<ul class="sub-menu">
<li><a href="http://www.website.com/pages/">page 1 sub</a></li>
<li><a href="http://www.website.com/pages/">page 2 sub</a></li>
<li><a href="http://www.website.com/pages/">page 3 sub</a></li>
<li><a href="http://www.website.com/pages/">page 4 sub</a>
<ul class="sub-menu">
<li><a href="http://www.website.com/pages/page4/">page 4 sub sub</a></li>
</ul>
</li>
<li><a href="http://www.website.com/pages/">page 5 sub</a></li>
<li><a href="http://www.website.com/pages/">page 6 sub</a></li>
<li><a href="http://www.website.com/pages/">page 7 sub</a></li>
<li><a href="http://www.website.com/pages/">page 8 sub</a></li>
<li><a href="http://www.website.com/pages/">page 9 sub</a></li>
<li><a href="http://www.website.com/pages/">page 10 sub</a></li>
</ul>
</li>
<li><a href="http://www.website.com/gallery/">Gallery</a>
<ul class="sub-menu">
<li><a href="http://www.website.com/gallery/">gallery 1 sub</a></li>
<li><a href="http://www.website.com/gallery/">gallery 2 sub</a></li>
<li><a href="http://www.website.com/gallery/">gallery 3 sub</a></li>
<li><a href="http://www.website.com/gallery/">gallery 4 sub</a></li>
</ul>
</li>
<li><a href="http://www.website.com/blog">Blog</a></li>
<li><a href="http://www.website.com/contact">Contact</a></li>
</div><!-- end sub_nav -->
- 01/27/11 11:09am
John Cotton says:OK, I get it now.
You need to put back in your test for parent ids. Probably the easiest thing is to stick the current code on PasteBin.com and I'll fix it.
JC - 01/27/11 11:19am
WP Answers says:Thanks John. So just to clarify, you would like me to paste my original sub_nav walker into pasteBin correct?
- 01/27/11 11:20am
John Cotton says:No wherever you've got to with it.
- 01/27/11 11:34am
WP Answers says:Sorry Brother I'm confused about what you need me to post.
Here are 3 pastebin links. Please let me know if this is what you're after:
Walker Class: http://pastebin.com/CdgRNpFn
Sub-Nav Output (using above Walker): http://pastebin.com/qCJay8vV
Regular wp_nav_menu() Output (no walker): http://pastebin.com/02Z9g3Zs
Cheers.
- 01/27/11 1:24pm
John Cotton says:Try this:
class sub_nav_walker extends Walker_Nav_Menu {
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
if ( !$element )
return;
global $post;
if ( ($depth == 0) && $element->object_id != $post->ID ) {
return;
}
parent::display_element($element, &$children_elements, $max_depth, $depth=0, $args, $output );
}
function start_el(&$output, $item, $depth, $args) {
global $post;
if ( ($depth == 0) && $item->object_id == $post->ID ) {
return;
}
parent::start_el($output, $item, $depth, $args);
}
function end_el(&$output, $item, $depth) {
if( $depth == 0 )
return;
parent::end_el($output, $item, $depth);
}
}
- 01/27/11 1:32pm
WP Answers says:Hi John,
Nothing at all gets outputted when using the above code. - 01/27/11 1:36pm
John Cotton says:
Please keep in mind the purpose of this custom walker is to display *only* all of the child pages of the particular parent section you're in, without displaying the parent page itself in the list.
Isn't that what's supposed to happen - except on the parent page?
Are you looking on a page that has a menu item?
Have a look here and you'll see it working on the right-hand side under test:
http://wp-test.dynamicarray.co.uk/?page_id=2 - 01/27/11 1:51pm
WP Answers says:Hi John,
The intended function of the walker to to display a simple sub-navigation. So no matter what page you are on in a particular section, it will show all of the pages in that section (excluding the parent-level page from the sub-nav list)
Please look at this as an example:
<ul class="sub-menu">
<li><a href="#"><span>Page 1</span></a></li>
<li><a href="#"><span>Page 2</span></a>
<ul class="sub-menu">
<li><a href="#"><span>Sub A</span></a></li>
<li><a href="#"><span>Sub B</span></a>
<ul class="sub-menu">
<li><a href="#"><span>Sub Sub 1</span></a></li>
<li><a href="#"><span>Sub Sub 2</span></a></li>
<li><a href="#"><span>Sub Sub 3</span></a></li>
</ul></li>
<li><a href="#"><span>Sub C</span></a></li>
</ul></li>
<li><a href="#"><span>Page 3</span></a></li>
<li><a href="#"><span>Page 4</span></a>
</ul>
If I were on Page 2 or any of its child pages, the sub-navigation should look like this:
<ul class="sub-menu">
<li><a href="#"><span>Sub A</span></a></li>
<li><a href="#"><span>Sub B</span></a>
<ul class="sub-menu">
<li><a href="#"><span>Sub Sub 1</span></a></li>
<li><a href="#"><span>Sub Sub 2</span></a></li>
<li><a href="#"><span>Sub Sub 3</span></a></li>
</ul></li>
<li><a href="#"><span>Sub C</span></a></li>
</ul></li>
</ul>
I hope this makes sense.
Cheers.
- 01/27/11 2:34pm
John Cotton says:Are Sub A and Sub Sub 1 etc all pages that are children of Page 2?
- 01/27/11 2:51pm
WP Answers says:Yes. All of them are child pages of "Page 2"
(Sub A, Sub B, Sub Sub 1, Sub Sub 2, Sub Sub 3, Sub C) - 01/27/11 3:30pm
John Cotton says:OK - I'm sure this is right now:
It displays all descendants of the top level page of the current page (including if the current page is the top level page)
class sub_nav_walker extends Walker_Nav_Menu {
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
if ( !$element )
return;
global $post;
if ( $depth == 0) {
if($element->object_id == $post->ID) {
$id_field = $this->db_fields['id'];
$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 ] );
}
return;
} else {
$_current_page = get_page( $post->ID );
if ( isset($_current_page->ancestors) && !in_array($element->object_id, (array) $_current_page->ancestors) ) {
return;
}
}
}
parent::display_element($element, &$children_elements, $max_depth, $depth=0, $args, $output );
}
function start_el(&$output, $item, $depth, $args) {
global $post;
if ( $depth == 0) {
$_current_page = get_page( $post->ID );
if ( isset($_current_page->ancestors) && in_array($item->object_id, (array) $_current_page->ancestors) )
return;
}
parent::start_el($output, $item, $depth, $args);
}
function end_el(&$output, $item, $depth) {
if ( $depth == 0) {
$_current_page = get_page( $post->ID );
if ( isset($_current_page->ancestors) && in_array($item->object_id, (array) $_current_page->ancestors) )
return;
}
parent::end_el($output, $item, $depth);
}
}
- 01/27/11 3:59pm
John Cotton says:Here's a slightly tidier version of the code:
class sub_nav_walker extends Walker_Nav_Menu {
var $_current_id;
var $_current_page;
function sub_nav_walker() {
global $post;
$this->_current_id = $post->ID;
$this->_current_page = get_page( $this->_current_id );
}
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
if ( !$element )
return;
if ( $depth == 0) {
if($element->object_id == $this->_current_id) {
$id_field = $this->db_fields['id'];
$id = $element->$id_field;
// descend only when there are childrens for this element
if ( 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 ] );
}
return;
} elseif ( isset($this->_current_page->ancestors) && !in_array($element->object_id, (array) $this->_current_page->ancestors) ) {
return;
}
}
parent::display_element($element, &$children_elements, $max_depth, $depth=0, $args, $output );
}
function start_el(&$output, $item, $depth, $args) {
if ( $depth == 0 && ( isset($this->_current_page->ancestors) && in_array($item->object_id, (array) $this->_current_page->ancestors) ) )
return;
parent::start_el($output, $item, $depth, $args);
}
function end_el(&$output, $item, $depth) {
if ( $depth == 0 && ( isset($this->_current_page->ancestors) && in_array($item->object_id, (array) $this->_current_page->ancestors) ) )
return;
parent::end_el($output, $item, $depth);
}
}
- 01/27/11 6:25pm
WP Answers says:Hi John,
Thanks a lot for your all your help so far, but both snippets you've posted are not working. They both list out every page in the navigation. Sorry if it seems like I'm being a pain in the a**. I don't mean to be, but the functions just aren't working.
I'll be back online in about 2 hours. - 01/27/11 6:36pm
John Cotton says:Fair enough.
I've got my code running on a test site and it works perfectly so I must be misunderstanding what you want to do (or what data is behind it) and it's hard to picture it without seeing your site.
If you stick some lines like this:
$output.= "line X: ".$item->object_id;
just before each of the returns you might get some idea why it's not displaying anything on your setup.
JC - 01/27/11 9:16pm
WP Answers says:Hi John,
The answer below is working perfectly for me. You stuck it out though, and I'd like to send you some money for your efforts. What is your e-mail address so I can send you some money via Paypal?
Thanks again for your hard work brother.
- 01/26/11 4:22pm
-

Last edited:
01/27/11
9:16pmrilwis says:Hi,
Please try this code: http://pastebin.com/Hxnf3WWb
Replace it with the old "sub_nav_walker" class.- 01/27/11 9:13pm
WP Answers says:This works like a charm!
Thank you so much brother.
- 01/27/11 9:13pm
This question has expired.
Sébastien | French WordpressDesigner, Lawrence Krubner had additional discourse to offer.
Current status of this question: Completed
Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.
If the asker does not get an answer then they have 10 days to request a refund.
