How can I do this:
I want to create menu using wp_nav_menu() and to customize it's output html a little. I want to put < i > in link < a > inside every < li > item of the menu.
I know that I can acomplish this using background-image of < li > items in css, but my goal is to use font icons inside navigation.
I also know that in order to acomplish this we can use walker function inside wp_nav_menu()or use wp_get_nav_menu_object() function, but I simply can't make it right to work.
Have you tried the before or the link_before options?
before will output before the <a> and link_before will output inside the <a> before the text.
http://codex.wordpress.org/Function_Reference/wp_nav_menu
$settings = array(
'before' => '<i class="icon"></i>',
'link_before' => '<i class="icon"></i>'
);
wp_nav_menu( $settings );
Use wp walker function and insert wp menu description there. Explaining more below -
Just put this code in your theme's functions.php file:
class fluent_themes_custom_walker_nav_menu extends Walker_Nav_Menu {
private $blog_sidebar_pos = "";
// add classes to ul sub-menus
function start_lvl( &$output, $depth = 0, $args = Array() ) {
// depth dependent classes
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = array(
'dropdown-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? '' : '' ),
'menu-depth-' . $display_depth
);
$class_names = implode( ' ', $classes );
// build html
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
// add main/sub classes to li's and links
function start_el( &$output, $item, $depth = 0, $args = Array(), $id = 0 ) {
global $wp_query, $wpdb;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// depth dependent classes
$depth_classes = array(
( $depth == 0 ? '' : '' ), //class for the top level menu which got sub-menu
( $depth >=1 ? '' : 'dropdown' ), //class for the level-1 sub-menu which got level-2 sub-menu
( $depth >=2 ? 'sub-sub-menu-item' : '' ), //class for the level-2 sub-menu which got level-3 sub-menu
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// passed classes
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// build html
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
// link attributes
$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 ) .'"' : '';
//$attributes .= ' class="' . ( $depth > 0 ? '' : '' ) . '"';
// Check if menu item is in main menu
$has_children = $wpdb->get_var("SELECT COUNT(meta_id)
FROM wp_postmeta
WHERE meta_key='_menu_item_menu_item_parent'
AND meta_value='".$item->ID."'");
if ( $depth == 0 && $has_children > 0 ) {
// These lines adds your custom class and attribute
$attributes .= ' class="dropdown-toggle"';
$attributes .= ' data-toggle="dropdown"';
$attributes .= ' data-hover="dropdown"';
$attributes .= ' data-animations="fadeInUp"';
}
$description = ! empty( $item->description ) ? '<i class="fa '.esc_attr( $item->description ).'" aria-hidden="true"></i>' : '';
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $description.$args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; //If you want the description to be output after <a>
//$item_output .= $description.$args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; //If you want the description to be output before </a>
// Add the caret if menu level is 0
if ( $depth == 0 && $has_children > 0 ) {
$item_output .= ' <i class="fa fa-caret-down"></i>';
}
$item_output .= '</a>';
$item_output .= $args->after;
// build html
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args, $id );
}
} //End Walker_Nav_Menu
See this line:
$description = ! empty( $item->description ) ? '<i class="fa '.esc_attr( $item->description ).'" aria-hidden="true"></i>' : '';
And this line:
$item_output .= $description.$args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
Here you see $item->desctiption is a variable. For example: if you put fa-user as menu item description of a menu. The html output of the above line will be:
The full html output of the menu will be something like this:
<ul class="top-nav nav-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-hover="dropdown" data-toggle="dropdown" data-animations="fadeInUp">
<i class="fa fa-user" aria-hidden="true"></i>My Profile
<i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu ">
<li><i class="icon-bargraph"></i> Dashboard</li>
<li><i class="icon-gears"></i> Profile Setting</li>
<li><i class="icon-heart"></i> Questions</li>
<li><i class="icon-lock"></i> Logout</li>
</ul>
</li>
</ul>
However, here is your wp nav menu code in your header.php file or in any other theme files:
wp_nav_menu( array('theme_location' => 'top_bar_login','container' => false,'container_id' => '','conatiner_class' => '','menu_class' => 'top-nav nav-right','echo' => true,'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>','depth' => 10, 'walker' => new fluent_themes_custom_walker_nav_menu) );
If you are not sure where is the description of wordpress menu, or you want more details with screenshot. You can read this article Adding Different Icons To Different Items Of WP Nav Menu
I'am also trying to add icons in my menu.
I've tried before and link_before options but I did't find the way to get any item's variables inside these args.
My aim is to get the following output using Title Attribute set in Custom Link from Customize Menus
<li id="menu-item-30" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-30">
<a title="social-facebook" href="http://facebook.com">
<i class="fi-social-facebook"></i>Facebook Page
</a>
</li>
<li id="menu-item-30" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-30">
<a title="social-google-plus+" href="http://plus.google.com">
<i class="fi-social-google-plus"></i>Google+ Page
</a>
</li>
I've also check the other answer but I don't know how to add thumbnails in the Custom Link of the Customize Menus.
paste code to the page where you output your nav
<?php
wp_nav_menu(
array(
'theme_location' => 'primary',
'container' => false,
'walker' => new my_nav_walker()
)
);
?>
and then call walker.php in functions.php
require get_template_directory().'/inc/walker.php';
copy class Walker_Nav_Menu in class-walker-nav-menu.php from wp-includes folder
change Walker_Nav_Menu class to my_nav_walker
and then inside function start_el of my_nav_walker class
make a variable called $description as:
$description = ! empty( $item->description ) ? '<i class="fa '.esc_attr( $item->description ).'" ></i>' : '';
Paste this code inside start_el function of my_nav_walker class
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $description.$args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
if you add fa-user in description of menu item from backend user icon appear between a href tag inside li
Related
I am having issues with a design I have had back from the front end dev, I have created and set up a custom nav but due to the nature of the nav e.g its set in columns it requires me to add another div into the sub menu so the standard below wont work
<?php wp_nav_menu( array( 'theme_location' => 'my-custom-menu', 'container_class' => 'custom-menu-class' ) );
?>
Is there actually a way of doing this or is it impossible as I have tried lots of code I found but nothing seems to work I have posted the HTML i have to work with below so you get an idea, any help would be appreciated
<ul class="sf-menu">
<li>HOME</li>
<li>ABOUT
<div class="sf-mega" style="display: none">
<nav id="subNav">
<!-- container -->
<div class="container">
<div class="colFour menu first">
<ul>
<li>Page 1</li>
<li>Vision</li>
</ul>
</div>
<div class="colFour menu">
<ul>
<li>Page 2</li>
<li>pag3 3</li>
</ul>
</div>
<div class="colFour menu">
<ul>
<li>page 4</li>
<li>page 5</li>
</ul>
</div>
<div class="colFour menu">
<ul>
<li>page 6</li>
</ul>
</div>
</div>
<!-- /container -->
</nav>
</div>
</li>
<li>Test 1</li>
<li>Test 2</li>
<li>Test 3</li>
<li>VACANCIES</li>
<li>CONTACT</li>
</ul>
Look at Walker_Nav_Menu class. It defines how a menu items are printed and gives you full control over it.
From the resource:
wp_nav_menu( array(
'menu' => 'Your menu',
'walker' => new New_Walker_Nav_Menu()
) );
/**
* Custom walker class.
*/
class New_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* Starts the list before the elements are added.
*
* Adds classes to the unordered list sub-menus.
*
* #param string $output Passed by reference. Used to append additional content.
* #param int $depth Depth of menu item. Used for padding.
* #param array $args An array of arguments. #see wp_nav_menu()
*/
function start_lvl( &$output, $depth = 0, $args = array() ) {
// Depth-dependent classes.
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = array(
'sub-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth
);
$class_names = implode( ' ', $classes );
// Build HTML for output.
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
/**
* Start the element output.
*
* Adds main/sub-classes to the list items and links.
*
* #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. Used for padding.
* #param array $args An array of arguments. #see wp_nav_menu()
* #param int $id Current item ID.
*/
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
global $wp_query;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// Depth-dependent classes.
$depth_classes = array(
( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// Passed classes.
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// Build HTML.
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
// Link attributes.
$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 ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
// Build HTML output and pass through the proper filter.
$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
$args->before,
$attributes,
$args->link_before,
apply_filters( 'the_title', $item->title, $item->ID ),
$args->link_after,
$args->after
);
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
I am using a custom walker for my menu which adds correct classes to parent ul/li/a elements. But, on every submenu LI and A tag i would like to remove all the styling but i cant figure out how to do it...
Any help would be greatly appreciated.
This is the code:
class CSS_Menu_Walker extends Walker {
var $db_fields = array('parent' => 'menu_item_parent', 'id' => 'db_id');
function start_lvl(&$output, $depth = 0, $args = array()) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul class='sub-menu'>\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul>\n";
}
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
global $wp_query;
$indent = ($depth) ? str_repeat("\t", $depth) : '';
$class_names = $value = '';
$classes = empty($item->classes) ? array() : (array) $item->classes;
/* Add active class */
if (in_array('current-menu-item', $classes)) {
$classes[] = 'active';
unset($classes['current-menu-item']);
}
/* Check for children */
$children = get_posts(array('post_type' => 'nav_menu_item', 'nopaging' => true, 'numberposts' => 1, 'meta_key' => '_menu_item_menu_item_parent', 'meta_value' => $item->ID));
if (!empty($children)) {
$classes[] = 'nav-item-parent';
}
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args));
$class_names = $class_names ? ' class="nav-item ' . 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 . $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="nav-link">';
$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);
}
function end_el(&$output, $item, $depth = 0, $args = array()) {
$output .= "</li>\n";
}
}
This is how the code should be outputed:
<li class="nav-item"> <!-- This is done properly -->
<a href="#" class="nav-link "> <!-- This is done properly -->
Top Level Link
</a>
</li>
<li class="nav-item nav-item-parent"><!-- This is done properly -->
<a href="#" class="nav-link "> <!-- This is done properly -->
Top Level Link
</a>
<ul class="sub-menu">
<!-- LI and A get css classes from their parents (aka, nav-item and nav-link) and i dont need any classes on them -->
<li>Child Link</li>
<li>Child Link</li>
<li>Child Link</li>
</ul>
</li>
Using only CSS, you can target the element using its parent's class:
Example:
.nav-item ul li{
//Your css declarations
}
.nav-item ul li a{
//Your css declarations
}
Inspect the element and see what properties it gets from its parents.
For example if it gets padding of 15px from the parent, you can force to change the value:
.nav-item ul li a{
padding: 0 !important;
}
Hope it helps
I am using a custom nav walker and would like to create a tree menu.
Example:
<ul class="nav nav-list">
<li><label class="tree-toggler nav-header">Header 1</label>
<ul class="nav nav-list tree">
<li>Link</li>
<li>Link</li>
<li><label class="tree-toggler nav-header">Header 1.1</label>
<ul class="nav nav-list tree">
<li>Link</li>
<li>Link</li>
<li><label class="tree-toggler nav-header">Header 1.1.1</label>
<ul class="nav nav-list tree">
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Now my goal is to check if a list item has a sub menu and if it has a sub menu I want to wrap the li's text in a label to create the collapse effect.
Here's my custom nav walker
class themeslug_walker_nav_menu extends Walker_Nav_Menu {
// add classes to ul sub-menus
function start_lvl( &$output, $depth ) {
// depth dependent classes
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = array(
'sub-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth
);
$class_names = implode( ' ', $classes );
// build html
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
// add main/sub classes to li's and links
function start_el( &$output, $item, $depth, $args ) {
global $wp_query;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// depth dependent classes
$depth_classes = array(
( $depth == 0 ? 'topElement' : 'parent' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// passed classes
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
if($depth === 0){
// build html
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '"><label class="tree-toggler">'.$item->title.'</label>';
}else{
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
};
// link attributes
$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 ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
$args->before,
$attributes,
$args->link_before,
apply_filters( 'the_title', $item->title, $item->ID ),
$args->link_after,
$args->after
);
// build html
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
This works fine but only for the li's in my first depth.
So I am looking for a check if the li has a sub menu or not
Is there anyone who can help me out please.
Thanks in advance!
Simply check if has children (to be checked what is actual name of this class) class exist in $classes array.
if ( $args->has_children && $depth === 0 ) {
// if item (li) has children and its depth is 0
}
In wp v6 Bool param from
$args->walker->has_children
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";
}
}
I am trying to alter Wordpress <?php wp_nav_menu( array( 'theme_location' => 'primary' ) ); ?> default output. What I wish to do:
Pages that have a submenu looks like this:
<li class="menu-item-has-children">
Page with Submenu
<ul class="sub-menu">
<li class="menu-item-has-children">
Level 1 Submenu Page
<ul class="sub-menu">
<li>Level 2 Submenu Page</li>
</ul>
</li>
</ul>
</li>
What I need it to look like:
<li class="menu-item-has-children">
Page with Submenu <i class="fa fa-caret-down"></i>
<ul class="sub-menu">
<li class="menu-item-has-children">
Level 1 Submenu Page <i class="fa fa-angle-right"></i>
<ul class="sub-menu">
<li>Level 2 Submenu Page</li>
</ul>
</li>
</ul>
</li>
Any ideea on how can I achieve this by CSS, jQuery or PHP?
If you're adding the extra markup just to add a down arrow, why not do this with CSS:
.menu-item-has-children > a {
padding-right:20px;
position:relative;
}
.menu-item-has-children > a:after {
content:"";
border:5px solid transparent;
border-top-color:grey;
position:absolute;
right:0;
top:50%;
}
Here's a JSfiddle.
To answer your specific question: you'll need PHP. Then some CSS styling.
To be more specific, you'll need to use a Custom Walker. With a custom walker, you can tell WordPress to use different styling for a menu, then another style for a sub-menu and so on, which is your exact case. You even have an example code there, which I'm pasting below:
class themeslug_walker_nav_menu extends Walker_Nav_Menu {
// add classes to ul sub-menus
function start_lvl( &$output, $depth = 0, $args = array() ) {
// depth dependent classes
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = array(
'sub-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth
);
$class_names = implode( ' ', $classes );
// build html
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
// add main/sub classes to li's and links
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
global $wp_query;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// depth dependent classes
$depth_classes = array(
( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// passed classes
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// build html
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
// link attributes
$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 ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
$args->before,
$attributes,
$args->link_before,
apply_filters( 'the_title', $item->title, $item->ID ),
$args->link_after,
$args->after
);
// build html
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
it looks you know some coding, so I won't extend on this, but it's easy to see you only need to modify this:
$classes = array(
'sub-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth
);
and/ or
// depth dependent classes
$depth_classes = array(
( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
'menu-item-depth-' . $depth
);
according to your needs. However, I STRONGLY RECOMMEND you read the page so you understand how it works, how you use it and when to use it.