wp_nav_menu change sub-menu class name?

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'.

Read More

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?

Related posts

Leave a Reply

13 comments

  1. 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()
    
  2. 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' );
    
  3. 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()
        )
    );
    
  4. 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'); 
    
  5. 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";
      }
    }
    
  6. 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');
    
  7. 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

  8. 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)

  9. 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

  10. 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.
    
  11. 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>

  12. 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.

  13. 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.