wp_nav_menu change sub-menu class name?

Wordpress

Wordpress Problem Overview


Is there a way to change the child <ul class="sub-menu"> generated by WordPress itself to a custom class name?

I know the parent <ul> you can remove or change the name with 'menu_class' => 'newname'.

I couldn't find the answer. Itried 'submenu_class' => 'customname'. It seems logic to me, but obviously that is no the right one.

any ideas?

Wordpress Solutions


Solution 1 - Wordpress

There is no option for this, but you can extend the 'walker' object that WordPress uses to create the menu HTML. Only one method needs to be overridden:

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"my-sub-menu\">\n";
  }
}

Then you just pass an instance of your walker as an argument to wp_nav_menu like so:

'walker' => new My_Walker_Nav_Menu()

Solution 2 - Wordpress

This is an old question and I'm not sure if the solution I'm going to mention was available by the time you asked, but I think it's worth mentioning. You can achieve what you want by adding a filter to nav_menu_submenu_css_class. See the example below - you can replace my-new-submenu-class by the class(es) you want:

function my_nav_menu_submenu_css_class( $classes ) {
    $classes[] = 'my-new-submenu-class';
    return $classes;
}
add_filter( 'nav_menu_submenu_css_class', 'my_nav_menu_submenu_css_class' );

Solution 3 - Wordpress

Choice of options.

  1. Switch the 'echo' argument to false and you can replace the class names.
    echo str_replace('sub-menu', 'menu menu_sub', wp_nav_menu( array(
        'echo' => false,
        'theme_location' => 'sidebar-menu',
        'items_wrap' => '<ul class="menu menu_sidebar">%3$s</ul>' 
      ) )
    );
  1. Since WP 4.8 we can use the nav_menu_submenu_css_class filter.
add_filter( 'nav_menu_submenu_css_class', 'my_custom_submenu_classnames', 10, 3 );
/**
 * Filters the CSS class(es) applied to a menu list element.
 *
 * @param string[] $classes Array of the CSS classes that are applied to the menu `<ul>` element.
 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
 * @param int      $depth   Depth of menu item. Used for padding.
 * @return string[]
 */
function my_custom_submenu_classnames( $classes, $args, $depth ) {
	// Here we can additionally use menu arguments.
	if ( 'header-menu' === $args->theme_location ) {
		$default_class_name_key = array_search( 'sub-menu', $classes );
		if ( false !== $default_class_name_key ) {
			unset( $classes[ $default_class_name_key ] );
		}
		$classes[] = 'header-submenu';
		$classes[] = "depth-{$depth}";
	}

	return $classes;
}
  1. Custom Menu walker:
class My_Nav_Menu_Walker extends Walker_Nav_Menu {
	/**
	 * Starts the list before the elements are added.
	 *
	 * @see Walker::start_lvl()
	 *
	 * @param string   $output Used to append additional content (passed by reference).
	 * @param int      $depth  Depth of menu item. Used for padding.
	 * @param stdClass $args   An object of wp_nav_menu() arguments.
	 */
	public function start_lvl( &$output, $depth = 0, $args = null ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$indent = str_repeat( $t, $depth );

		// ! You default class names.
		$classes = array( 'sub-menu', 'my-class' );
		// ! Example of using arguments.
		if ( 'header-menu' === $args->theme_location ) {
			$default_class_name_key = array_search( 'sub-menu', $classes );
			if ( false !== $default_class_name_key ) {
				unset( $classes[ $default_class_name_key ] );
			}
			$classes[] = 'header-submenu';
			$classes[] = "depth-{$depth}";
		}

		/**
		 * Filters the CSS class(es) applied to a menu list element.
		 *
		 * @since 4.8.0
		 *
		 * @param string[] $classes Array of the CSS classes that are applied to the menu `<ul>` element.
		 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
		 * @param int      $depth   Depth of menu item. Used for padding.
		 */
		$class_names = implode( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

		$output .= "{$n}{$indent}<ul{$class_names}>{$n}";
	}
}

It remains to include the file with an custom walker and point it to the menu:

wp_nav_menu(
    array(
        'theme_location' => 'header-menu',
        'walker'         => new My_Nav_Menu_Walker()
    )
);

Solution 4 - Wordpress

You can use WordPress preg_replace filter (in your theme functions.php file) example:

function new_submenu_class($menu) {    
    $menu = preg_replace('/ class="sub-menu"/','/ class="yourclass" /',$menu);        
    return $menu;      
}

add_filter('wp_nav_menu','new_submenu_class'); 

Solution 5 - Wordpress

Here's an update to what Richard did that adds a "depth" indicator. The output is level-0, level-1, level-2, etc.

class UL_Class_Walker extends Walker_Nav_Menu {
  function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"level-".$depth."\">\n";
  }
}

Solution 6 - Wordpress

You don't need to extend the Walker. This will do:

function overrideSubmenuClasses( $classes ) {
    $classes[] = 'myclass1';
    $classes[] = 'myclass2';

    return $classes;
}
add_action('nav_menu_submenu_css_class', 'overrideSubmenuClasses');

Solution 7 - Wordpress

Like it always is, after having looked for a long time before writing something to the site, just a minute after I posted here I found my solution.

It thought I'd share it here so someone else can find it.

//Add "parent" class to pages with subpages, change submenu class name, add depth class

	class Prio_Walker extends Walker_Nav_Menu {
	function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ){
	    $GLOBALS['dd_children'] = ( isset($children_elements[$element->ID]) )? 1:0;
        $GLOBALS['dd_depth'] = (int) $depth;
        parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }
	
	 function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"children level-".$depth."\">\n";
  }
}

add_filter('nav_menu_css_class','add_parent_css',10,2);
function  add_parent_css($classes, $item){
     global  $dd_depth, $dd_children;
     $classes[] = 'depth'.$dd_depth;
     if($dd_children)
         $classes[] = 'parent';
    return $classes;
}

//Add class to parent pages to show they have subpages (only for automatic wp_nav_menu)

function add_parent_class( $css_class, $page, $depth, $args )
{
   if ( ! empty( $args['has_children'] ) )
       $css_class[] = 'parent';
   return $css_class;
}
add_filter( 'page_css_class', 'add_parent_class', 10, 4 );

This is where I found the solution: Solution in WordPress support forum

Solution 8 - Wordpress

I had to change:

function start_lvl(&$output, $depth)

to:

function start_lvl( &$output, $depth = 0, $args = array() )

Because I was getting an incompatibility error:

Strict Standards: Declaration of My_Walker_Nav_Menu::start_lvl() should be compatible with Walker_Nav_Menu::start_lvl(&$output, $depth = 0, $args = Array)

Solution 9 - Wordpress

This may be useful to you

How to add a parent class for menu item

function wpdocs_add_menu_parent_class( $items ) {
$parents = array();

// Collect menu items with parents.
foreach ( $items as $item ) {
    if ( $item->menu_item_parent && $item->menu_item_parent > 0 ) {
        $parents[] = $item->menu_item_parent;
    }
}

// Add class.
foreach ( $items as $item ) {
    if ( in_array( $item->ID, $parents ) ) {
        $item->classes[] = 'menu-parent-item';
    }
}
return $items;
 }
add_filter( 'wp_nav_menu_objects', 'wpdocs_add_menu_parent_class' );

/**
 * Add a parent CSS class for nav menu items.
 * @param array  $items The menu items, sorted by each menu item's menu order.
 * @return array (maybe) modified parent CSS class.
*/

Adding Conditional Classes to Menu Items

function wpdocs_special_nav_class( $classes, $item ) {
    if ( is_single() && 'Blog' == $item->title ) {
    // Notice you can change the conditional from is_single() and $item->title
    $classes[] = "special-class";
}
return $classes;
}
add_filter( 'nav_menu_css_class' , 'wpdocs_special_nav_class' , 10, 2 );

For reference : click me

Solution 10 - Wordpress

You can just use a Hook

add_filter( 'nav_menu_submenu_css_class', 'some_function', 10, 3 );
function some_function( $classes, $args, $depth ){
	foreach ( $classes as $key => $class ) {
	if ( $class == 'sub-menu' ) {
		$classes[ $key ] = 'my-sub-menu';
	}
}

return $classes;
}

where

$classes(array) - The CSS classes that are applied to the menu <ul> element.
$args(stdClass) - An object of wp_nav_menu() arguments.
$depth(int) - Depth of menu item. Used for padding.

Solution 11 - Wordpress

in the above i need a small change which i am trying to place but i am not able to do that, your output will look like this

<ul>
<li id="menu-item-13" class="depth0 parent"><a href="#">About Us</a>
<ul class="children level-0">
	<li id="menu-item-17" class="depth1"><a href="#">Sample Page</a></li>
	<li id="menu-item-16" class="depth1"><a href="#">About Us</a></li>
</ul>
</li>
</ul> 

what i am looking for

<ul>
<li id="menu-item-13" class="depth0"><a class="parent" href="#">About Us</a>
<ul class="children level-0">
	<li id="menu-item-17" class="depth1"><a href="#">Sample Page</a></li>
	<li id="menu-item-16" class="depth1"><a href="#">About Us</a></li>
</ul>
</li>
</ul> 

in the above one i have placed the parent class inside the parent anchor link that <li id="menu-item-13" class="depth0"><a class="parent" href="#">About Us</a>

Solution 12 - Wordpress

I would suggest to replace your cutomclass css class name to sub-menu. use find and replace: ie. find: .customclass replace with .sub-menu, works for me.

Solution 13 - Wordpress

To change the default "sub-menu" class name, there is simple way. You can just change it in wordpress file.

location : www/project_name/wp-includes/nav-menu-template.php.

open this file and at line number 49, change the name of sub-menu class with your custom class.

Or you can also add your custom class next to sub-menu.

Done.

It worked for me.I used wordpress-4.4.1.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCamView Question on Stackoverflow
Solution 1 - WordpressRichard MView Answer on Stackoverflow
Solution 2 - WordpressFlavioEscobarView Answer on Stackoverflow
Solution 3 - WordpressvralleView Answer on Stackoverflow
Solution 4 - WordpressPushpendu MondalView Answer on Stackoverflow
Solution 5 - WordpressJonathan WoldView Answer on Stackoverflow
Solution 6 - WordpressLucas BustamanteView Answer on Stackoverflow
Solution 7 - WordpresszartgesottenView Answer on Stackoverflow
Solution 8 - WordpressVinnie JamesView Answer on Stackoverflow
Solution 9 - Wordpressnaveenkumar.sView Answer on Stackoverflow
Solution 10 - WordpressArtemiy EgorovView Answer on Stackoverflow
Solution 11 - WordpressKundan SInghView Answer on Stackoverflow
Solution 12 - WordpressRicardOView Answer on Stackoverflow
Solution 13 - WordpressSandeep SinghView Answer on Stackoverflow