I have a menu in the WordPress backend that looks like this:
What we do
Solutions
Solution 1
Solution 2
Services
Service 1
Contact
Items What we do and Contact are top level links and I only want these level 1 links to show in my main header.
As such, I created a custom walker to achieve this:
<?php
class simple_header extends Walker_Nav_Menu{
/*
* start_lvl : Responsible for start of a new level, such as <ul>
* end_lvl : Responsible for end of a level, such as </ul>
* start_el : Responsible for start of inner element, such as <li>
* end_el : Responsible for end of inner element, such as <\li>
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
}
public function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
}
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = $item->ID;
$class_names = 'header__nav-menu-li';
if (in_array('current-menu-item', $classes)) {
$class_names .= ' header__nav-menu-li--active';
}
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
if ($depth === 0) {
$output .= $indent . '<li data-test' . $class_names .'>';
}
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
// New
if ($depth === 0) {
$atts['class'] = 'header__nav-menu-anchor link-header';
}
if (in_array('current-menu-item', $item->classes)) {
$atts['class'] .= ' link-header--active';
}
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
/** This filter is documented in wp-includes/post-template.php */
$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 );
}
public function end_el( &$output, $item, $depth = 0, $args = array() ) {
if ($depth === 0) {
$output .= "</li>\n";
}
}
}
?>
However, with the above walker, it still prints out the child markup. The HTML looks like this:
<li class="header__nav-menu-li header__nav-menu-li--active">
What we do
Solutions
Services
</li>
I have defined $depth === 0 in my walker, so unsure why it's printing child links?
Normally, at the start_lvl and end_lvl you would output <ul> and </ul> tags respectively to start and end a new list.
Every item in your menu consists of an anchor tag wrapped inside a list item:
<li>Target</li>
In your code you only have an 'if' statement around the code that adds the list item tags to the output.
On the last line of your start_el function you still add the anchor.
Instead of wrapping an if statement around the parts where you want to output markup, my advice would be to add the following line of code as the first line of your start_el function:
if($depth !== 0) { return; }
That way you skip execution of code for items that are not in the first level.
Related
I have this pretty basic custom walker for my nav. Is there any way to split the child menu items into columns, for example, if we have 6 child nav items in the first nav item, how can I put 3 in one column and 3 in another column?
I know I can do that with CSS, but I would extend it further to group items based on their count and that won't work with CSS.
class SC_Menu_Walker extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth=0, $args=[], $id=0) {
global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$this->curItem = $item;
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$class_names = ' class="'. esc_attr( $class_names ) . '"';
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
if ($item->url && $item->url != '#') {
$output .= '<a href="' . $item->url . '">';
} else {
$output .= '<span>';
}
$output .= $item->title;
}
function start_lvl(&$output, $depth = 0, $args = array()) {
$sc_page_item_label = get_post_meta( $this->curItem->object_id, '_sc_page_item_label', true );
$sc_page_item_description = get_post_meta( $this->curItem->object_id, '_sc_page_item_description', true );
$sc_page_item_url = get_post_meta( $this->curItem->object_id, '_sc_page_item_url', true );
$sc_page_item_page_url = get_page_link($sc_page_item_url);
$secondMenuClass="sub-menu__list o-sm-col-12 o-lg-col-7 u-font-size-medium";
$wrapperScrollStart="";
if ($depth == 1) {
$secondMenuClass="sub-menu__list sub-menu__list--scrollbar";
$wrapperScrollStart="<div class='sub-menu__inner'>";
$wrapperScrollEnd ='</div>';
}
$output .= '<div class="sub-menu sub">
<div class="o-custom-container">
<div class="sub-menu__wrapper_info o-sm-col-12 o-lg-col-5"><div class="o-lg-col-7"><h5>'.$sc_page_item_label.'</h5> <div class="u-font-size-medium">'.$sc_page_item_description.'</div></div>
<div>
<div class="c-learn-more-holder">
<div class="c-learn-more">
<div class="c-learn-more-title">Learn more</div>
</div>
</div>
</div>
</div>'.$wrapperScrollStart.'<ul class="'.$secondMenuClass.'">';
}
//end of the sub menu wrap
function end_lvl(&$output, $depth = 0, $args = array()) {
$wrapperScrollEnd = '';
if ($depth == 1) {
$wrapperScrollEnd ='</div>';
}
$wrapperScrollEnd ='</div>';
$output .= '</ul>'.$wrapperScrollEnd.'</div>';
}
}
The reason for posting this here (and not on wordpress stackexchange) is because I have a feeling that this might be related to invalid PHP. It seems more users are familiar with php coding here.
I have a strange problem where I am not able to add an image to a post through the media library. I am able to upload an image, but wordpress does not display the previously uploaded images in the media library, so I cannot insert any of them to a new post. Basically the media library does not fetch any images.
So far I have found out that this problem is spesific to my theme, and that removing this line: require get_template_directory() . '/inc/css_menu_walker.php'; from my functions.php solves the problem.
However I need to load the custom walker to render the menu. So what is wrong here? As far as I can see I have loaded the template properly and I cannot find any errors in css_menu_walker.php:
<?php
class CSS_Menu_Maker_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>\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[] = 'has-sub';
}
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = $class_names ? ' class="' . 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 .'><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 = 0, $args = array() ) {
$output .= "</li>\n";
}
}
I did not write the custom walker myself. I got it from here: http://cssmenumaker.com/blog/wordpress-3-drop-down-menu-tutorial and it seems this page has not been updated since 2013. This leads me to think that the code might be outdated and incompatible with the current version of wordpress.
Wordpress version does not matter. I have the same problem in 4.5.2 and 4.4.3.
In my functions.php I replaced:
// Include the custom walker navigation
require get_template_directory() . '/inc/css_menu_walker.php';
with
/**
* Custom walker navigation
*/
require get_template_directory() . '/inc/css-menu-walker.php';
Complete difference here: https://www.diffnow.com/?report=ba2d5
I also had some indentation before <?php at the start of my css-menu-walker.php which I removed. This fixed the problem.
I wanted to add number of posts for menu items that are categories.
For example:
Category 1 (5)
Category 2 (8)
Category 3 (2)
etc...
I edit walker:
get number:
$item_count = $depth == 0 ? get_posts( array(
'post_type' => 'post',
'numberposts' => -1,
'orderby' => 'category',
'order' => 'ASC'
) ) : false;
output:
$item_output .= $item_count ? ' <span>(' . count( $item_count ) . ')</span>' : '';
The result is a postscript to each menu item the number of all posts.
How to modify?
use wp_list_categories
<?php wp_list_categories(array('show_count' => true)); ?>
UPDATE for your instance:
with the full code from the link you provided in the comments we add an if statement right before the closing <a>
if($item->type == 'taxonomy'){
$cat = get_category( $item->object_id);
$item_output .= ' ('.$cat->count.')</a>';
}
FULL CODE:
/* Bootstrap_Walker for Wordpress
* Author: George Huger, Illuminati Karate, Inc
* More Info: http://illuminatikarate.com/blog/bootstrap-walker-for-wordpress
*
* Formats a Wordpress menu to be used as a Bootstrap dropdown menu (http://getbootstrap.com).
*
* Specifically, it makes these changes to the normal Wordpress menu output to support Bootstrap:
*
* - adds a 'dropdown' class to level-0 <li>'s which contain a dropdown
* - adds a 'dropdown-submenu' class to level-1 <li>'s which contain a dropdown
* - adds the 'dropdown-menu' class to level-1 and level-2 <ul>'s
*
* Supports menus up to 3 levels deep.
*
*/
class Bootstrap_Walker extends Walker_Nav_Menu
{
/* Start of the <ul>
*
* Note on $depth: Counterintuitively, $depth here means the "depth right before we start this menu".
* So basically add one to what you'd expect it to be
*/
function start_lvl(&$output, $depth)
{
$tabs = str_repeat("\t", $depth);
// If we are about to start the first submenu, we need to give it a dropdown-menu class
if ($depth == 0 || $depth == 1) { //really, level-1 or level-2, because $depth is misleading here (see note above)
$output .= "\n{$tabs}<ul class=\"dropdown-menu\">\n";
} else {
$output .= "\n{$tabs}<ul>\n";
}
return;
}
/* End of the <ul>
*
* Note on $depth: Counterintuitively, $depth here means the "depth right before we start this menu".
* So basically add one to what you'd expect it to be
*/
function end_lvl(&$output, $depth)
{
if ($depth == 0) { // This is actually the end of the level-1 submenu ($depth is misleading here too!)
// we don't have anything special for Bootstrap, so we'll just leave an HTML comment for now
$output .= '<!--.dropdown-->';
}
$tabs = str_repeat("\t", $depth);
$output .= "\n{$tabs}</ul>\n";
return;
}
/* Output the <li> and the containing <a>
* Note: $depth is "correct" at this level
*/
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;
/* If this item has a dropdown menu, add the 'dropdown' class for Bootstrap */
if ($item->hasChildren) {
$classes[] = 'dropdown';
// level-1 menus also need the 'dropdown-submenu' class
if($depth == 1) {
$classes[] = 'dropdown-submenu';
}
}
/* This is the stock Wordpress code that builds the <li> with all of its attributes */
$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;
/* If this item has a dropdown menu, make clicking on this link toggle it */
if ($item->hasChildren && $depth == 0) {
$item_output .= '<a'. $attributes .' class="dropdown-toggle" data-toggle="dropdown">';
} else {
$item_output .= '<a'. $attributes .'>';
}
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
/* Output the actual caret for the user to click on to toggle the menu */
if ($item->hasChildren && $depth == 0) {
$item_output .= '<b class="caret"></b></a>';
}elseif($item->type == 'taxonomy'){
$cat = get_category( $item->object_id);
$item_output .= ' ('.$cat->count.')</a>';
} else {
$item_output .= '</a>';
}
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
return;
}
/* Close the <li>
* Note: the <a> is already closed
* Note 2: $depth is "correct" at this level
*/
function end_el (&$output, $item, $depth, $args)
{
$output .= '</li>';
return;
}
/* Add a 'hasChildren' property to the item
* Code from: http://wordpress.org/support/topic/how-do-i-know-if-a-menu-item-has-children-or-is-a-leaf#post-3139633
*/
function display_element ($element, &$children_elements, $max_depth, $depth = 0, $args, &$output)
{
// check whether this item has children, and set $item->hasChildren accordingly
$element->hasChildren = isset($children_elements[$element->ID]) && !empty($children_elements[$element->ID]);
// continue with normal behavior
return parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
}
}
If you are using some custom post taxonomie you can change the code for this one :
if ($item->type == 'taxonomy'){
$term = get_term($item->object_id);
$item_output .= '<div class="meni-item-badge"> '.$term->count .'</div>';
}
Hope this help someone !!
Regards Mario
Is there any way to pass a variable from start_el to start_lvl? I want to place the menu description in the wrapper of the submenu items.
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'><div class='menu-image-container'><div class='menu-image'></div></div>\n";
}
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "$indent<div class='clear'></div></ul>\n";
}
}
I've tried to store the description as a variable in start_el, and access it using global in start_lvl... but it returns nothing.
I'm really desperate for help on this, as so far I've had no results, or responses to this question in terms of answers.
Can someone please help me with this? There are definitely some of you who know how to use the Wordpress walker menu.
Here is the way to use this code and it will display the parent description.
Steps for using of it.
1: Copy the below class and past into the functions.php file.
2: Call the menu like
wp_nav_menu(array(
'menu_id'=>'',
'menu_class'=>'',
'container'=>'',
'theme_location'=>'#enter theme location#',
'walker'=> new customize_menu_walker()
));
<br/>
3:See the result.
class customize_menu_walker extends Walker_Nav_Menu
{
function start_el(&$output, $item, $depth, $args)
{
global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
// $dbclasses=$item->classes;
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$dbclasses=$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="'. esc_attr( $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;
//specially for two menu
//specially for two menu
//convert string to array
$dbclassesArr = explode(" ",$dbclasses);
$item_output .= '<a'. $attributes .'>';
if(in_array("menu-item-has-children", $dbclassesArr)){
$description = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';//description display here.
}
$item_output .= $args->link_before .apply_filters( 'the_title', $item->title, $item->ID );
$item_output .= $description.$args->link_after;
$item_output .= ' '.'</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
Now after updating this function, your menu will show the parent menu description.
Here is the code.
Thanking you.
I have managed to find the solution to this question that seemed to go unanswered for days here - How can I add parent menu description to my Wordpress menu?
I want to assign specefic class in menu list to its child anchor.
for example:
<li class="icon-home list-item">
Home
</li>
I want to convert above output to this (Remove class icon-home from list and assign it to its child anchor):
<li class="list-item">
<a class="icon-home" href="index.php">Home</a>
</li>
What I want to do ??
Search for matching class icon- in wordpress menu array, then set it to its child anchor and then unset it from list.
I am using a code which search for a class divider in menu array and then convert it to a new list with class="divider". Here is that code:
// managing divider: add divider class to an element to get a divider before it.
$divider_class_position = array_search('divider', $classes);
if($divider_class_position !== false){
$output .= "<li class=\"divider\"></li>\n";
unset($classes[$divider_class_position]);
}
Here is my all code for wordpress menu override, Please have a look before answering:
<?php
/**
* Extended Walker class for use with the
* Twitter Bootstrap toolkit Dropdown menus in Wordpress.
* Edited to support n-levels submenu.
* #author johnmegahan https://gist.github.com/1597994, Emanuele 'Tex' Tessore https://gist.github.com/3765640
*/
class NavMenuWalker extends Walker_Nav_Menu {
function start_lvl( &$output, $depth ) {
$indent = str_repeat( "\t", $depth );
$submenu = ($depth > 0) ? ' sub-menu' : '';
$output .= "\n$indent<ul class=\"dropdown-menu$submenu depth_$depth\">\n";
}
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$li_attributes = '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
// managing divider: add divider class to an element to get a divider before it.
$divider_class_position = array_search('divider', $classes);
if($divider_class_position !== false){
$output .= "<li class=\"divider\"></li>\n";
unset($classes[$divider_class_position]);
}
$classes[] = ($args->has_children) ? 'dropdown' : '';
$classes[] = ($item->current || $item->current_item_ancestor) ? 'active' : '';
$classes[] = 'menu-item-' . $item->ID;
if($depth && $args->has_children){
$classes[] = 'dropdown-submenu';
}
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names . $li_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 .= ($args->has_children) ? ' class="dropdown-toggle" data-toggle="dropdown"' : '';
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= ($depth == 0 && $args->has_children) ? ' <b class="caret"></b></a>' : '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
//v($element);
if ( !$element )
return;
$id_field = $this->db_fields['id'];
//display this element
if ( is_array( $args[0] ) )
$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );
else if ( is_object( $args[0] ) )
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
$cb_args = array_merge( array(&$output, $element, $depth), $args);
call_user_func_array(array(&$this, 'start_el'), $cb_args);
$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 ] );
}
if ( isset($newlevel) && $newlevel ){
//end the child delimiter
$cb_args = array_merge( array(&$output, $depth), $args);
call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
}
//end this element
$cb_args = array_merge( array(&$output, $element, $depth), $args);
call_user_func_array(array(&$this, 'end_el'), $cb_args);
}
}
You have to look for:
$item_output .= '<a'. $attributes .'>';
and replace it with:
$item_output .= '<a'. $attributes .' class="'.$classes[0].'">';
Now it changes <li class="icon-home list-item"> to <a href="index.php" class="icon-home">
On $attributes just display Link active and hover effect no one specific class define in link .