I've entered the description into the parent menu items in Wordpress, but they're not showing on my theme.
I know that a walker class can be used to make changes to the menu, but I don't know how to code it.
Here is what I want to achieve:
<nav id="main-menu" role="navigation">
<div class="menu-main-menu-container">
<ul id="menu-main-menu" class="menu">
<!-- REPEATING MENU ITEM START -->
<li class="menu-parent-item"><a>Face</a>
<ul class="sub-menu">
<li class="menu-image-container">
<div class="menu-image"></div>
<div class="menu-description">[Face menu item description]</div>
</li>
<li class="heading">Face</li>
<ul class="sub-menu">
<li class="menu-item">Sub menu 1</li>
<li class="menu-item">Sub menu 2</li>
<li class="menu-item">Sub menu 3</li>
</ul>
<li class="heading">Ear</li>
<ul class="sub-menu">
<li class="menu-item">Sub menu 1</li>
<li class="menu-item">Sub menu 2</li>
<li class="menu-item">Sub menu 3</li>
</ul>
<li class="heading">Eyes</li>
<ul class="sub-menu">
<li class="menu-item">Sub menu 1</li>
<li class="menu-item">Sub menu 2</li>
<li class="menu-item">Sub menu 3</li>
</ul>
</ul>
</li>
<!-- REPEATING MENU ITEM END -->
</ul>
</div>
As you can see, I only want the description of the parent menu item to be shown, but it needs to be within the first ul.sub-menu
within the parent list item.
How could I code a walker that uses start_lvl
, start_el
and end_lvl
to handle this efficiently?
ok i'm not sure if this is fool proof but here's my try:
so you would need to make sure description is on in the admin menu console (in the 'screen options' dropdown)
Your first menu item should be Face then Face again as its child then sub neu items as children to that.
Hope this helps!
in your functions.php
class Description_Walker extends Walker_Nav_Menu
{
/**
* Start the element output.
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. May be used for padding.
* @param array $args Additional strings.
* @return void
*/
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output )
{
$id_field = $this->db_fields['id'];
if ( is_object( $args[0] ) ) {
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_lvl( &$output, $depth=0, $args=array() ) {
// depth dependent classes
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth); // because it counts the first submenu as 0
$classes = array('sub-menu');
$class_names = implode( ' ', $classes );
// build html
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
function start_el(&$output, $item, $depth = 0, $args = array(), $current_object_id = 0)
{
$classes = empty ( $item->classes ) ? array () : (array) $item->classes;
$class_names = join(
' '
, apply_filters(
'nav_menu_css_class'
, array_filter( $classes ), $item
)
);
if ($args->has_children && $depth == 0){
! empty ( $class_names )
and $class_names = ' class="menu-parent-item"';
}else if($depth == 1){
! empty ( $class_names )
and $class_names = ' class="heading"';
}else{
! empty ( $class_names )
and $class_names = ' class="'. esc_attr( $class_names ) .'"';
}
$output .= "<li id='menu-item-$item->ID' $class_names>" ;
$attributes = '';
! empty( $item->attr_title )
and $attributes .= ' title="' . esc_attr( $item->attr_title ) .'"';
! empty( $item->target )
and $attributes .= ' target="' . esc_attr( $item->target ) .'"';
! empty( $item->xfn )
and $attributes .= ' rel="' . esc_attr( $item->xfn ) .'"';
! empty( $item->url )
and $attributes .= ' href="' . esc_attr( $item->url ) .'"';
// insert description for top level elements only
// you may change this
$description = ( ! empty ( $item->description ) and 0 == $depth )
? '<div class="menu-description">' . esc_attr( $item->description ) . '</div>' : '';
$title = apply_filters( 'the_title', $item->title, $item->ID );
if ( $depth == 0) {//top level items
$item_output = $args->before.$title.'</li><ul class="sub-menu"><li class="menu-image-container"><div class="menu-image"></div>'.$description.'</li>';
}
else if( $depth == 1){
$item_output = $args->before.$title.'</li>';
}
else{//everything else
$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 .= apply_filters(
'walker_nav_menu_start_el'
, $item_output
, $item
, $depth
, $args
);
}
}
in your header (or wherever your menu is)
<nav id="main-menu" role="navigation"><?php wp_nav_menu( array('menu' => 'Main', 'container' => 'div', 'container_class' => 'menu-main-menu-container', 'menu_id' => 'menu-main-menu', 'walker' => new Description_Walker )); ?></nav>
I managed to get the menu to display as before, whilst adding the parent menu description exactly where I wanted by simply just adding to what I already had.
I already had a walker that created the sub-menu image container, and the description container
// Submenu walker to add image
class submenu_walker extends Walker_Nav_Menu {
function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul class='sub-menu'><li class='menu-image-container'><div class='menu-image'></div><div class='menu-description'></div></li>\n";
}
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "$indent<li><div class='clear'></div></li></ul>\n";
}
}
Then I managed to find a function that uses start_el
and could assign the description to a variable, but not output it, then just output the $item_output
as normal.
function add_menu_description( $item_output, $item, $depth, $args ) {
$description = __( $item->post_content );
return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'add_menu_description', 10, 4);
Of course now I needed to use $description
within my other submenu walker function, so I just created a global variable in both, and the output is exactly what I'm after!
FINAL OUTPUT
function add_menu_description( $item_output, $item, $depth, $args ) {
global $description;
$description = __( $item->post_content );
return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'add_menu_description', 10, 4);
// Submenu walker to add image
class submenu_walker extends Walker_Nav_Menu {
function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
global $description;
$output .= "\n$indent<ul class='sub-menu'><li class='menu-image-container'><div class='menu-image'></div><div class='menu-description'>".$description."</div></li>\n";
}
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "$indent<li><div class='clear'></div></li></ul>\n";
}
}