7

i want to add a class to the a tag of an sub-menu and a b tag inside the link.

WordPress gives me this code:

<li id="menu-item-72" class="menu-item menu-item-type-post_type menu-item-object-page dropdown menu-item-72"><a
    href="#">Link</a>
  <ul class="dropdown-menu"></ul>

And i want this:

<li id="menu-item-72" class="menu-item menu-item-type-post_type menu-item-object-page dropdown menu-item-72"> <a
    href="#" class="dropdown-toggle" data-toggle="dropdown">Link <b class="caret"></b></a>
  <ul class="dropdown-menu"></ul>

Does anybody know a solution for that?

Ruvee
  • 8,611
  • 4
  • 18
  • 44
Cray
  • 5,307
  • 11
  • 70
  • 166

5 Answers5

11

Look at this answer, it explains how to add custom HTML to the wordpress menus: https://stackoverflow.com/a/12251157/1627227

EDIT:

I've put together an example to fit your question. You can place it into functions.php. Note the comments, they explain where to add your custom code.

class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {

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

      // Change sub-menu to dropdown menu
      $output .= "\n$indent<ul class=\"dropdown-menu\">\n";
  }

  function start_el ( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    // Most of this code is copied from original Walker_Nav_Menu
    global $wp_query, $wpdb;
    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

    $class_names = $value = '';

    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $classes[] = 'menu-item-' . $item->ID;

    $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 ) . '"' : '';

    $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."'");

    $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        ) .'"' : '';

    // Check if menu item is in main menu
    if ( $depth == 0 && $has_children > 0  ) {
        // These lines adds your custom class and attribute
        $attributes .= ' class="dropdown-toggle"';
        $attributes .= ' 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;

    // Add the caret if menu level is 0
    if ( $depth == 0 && $has_children > 0  ) {
        $item_output .= ' <b class="caret"></b>';
    }

    $item_output .= '</a>';
    $item_output .= $args->after;

    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
  }

}

After you have this in place, you have to go to the point where your menu (wp_nav_menu()) is called. In the answer I've linked to, there's the full function call to wp_nav_menu. However you'll have to add this line: 'walker' => new Custom_Walker_Nav_Menu to the arguments array, to use your custom walker object on that specific menu.

Hope you got it ;)

Community
  • 1
  • 1
martinczerwi
  • 2,837
  • 23
  • 23
  • Sorry, i don't get it. Where should i insert the "class="dropdown-toggle" data-toggle="dropdown"" for a parent element? and where the b-tag inside the link? – Cray Oct 31 '12 at 17:06
  • I've added an example which applies the changes you want to the code. If there's still anything unclear, would you please tell me, if you handcoded your theme, or if you're using a downloaded theme. Also the code requires, you're using the [WordPress Menus](http://codex.wordpress.org/Navigation_Menus) and `wp_nav_menu` to call your menus. – martinczerwi Nov 02 '12 at 09:48
  • Ah, thanks a lot! But i need the class "dropdown-menu" only in parent menu items. The attribute "data-toggle="dropdown"" should only appear in links of parent menu items. And inside this link, i need this code: `` In the `
      ` of the sub-menu i need the class "dropdown-menu" The question is: how can i check if there is a submenu or if the menu item is a parent item?
    – Cray Nov 02 '12 at 16:35
  • I'm really sorry, I've missed the caret and also had an error in the HTML syntax. I've edited the code above. You can determine if you're in the main menu by the `$depth`-Variable. `$depth` is the menu level, for the main menu it's 0, the deeper the level, the higher the number. – martinczerwi Nov 02 '12 at 16:55
  • Thanks, but now i have the attributes and classes in every item with depth=0. I need them only if there is a sub-menu. is there an variable for that? – Cray Nov 02 '12 at 17:08
  • I just tried to figure out a solution. Unfortunately it only works when the items in the menu are childpages of the main pages. It doesn't work for submenus created in the menu admin page. Couldn't figure out, how to know if there are submenus in the menu admin page. I don't know about your dropdown, but how about a workaround: Instead of asking for the data-toggle attribute, can you ask if the list item has a `
      ` in it?
    – martinczerwi Nov 05 '12 at 17:18
  • Edited the code to include Khairul's addition (but my edit awaits moderation). – MastaBaba Jul 07 '13 at 09:23
  • So there is a more accurate way to fire the "$has-children" variable - currently set to static values. Simply replace the current $has-children with: $has_children = $wpdb->get_var( $wpdb->prepare(" SELECT COUNT(*) FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %d ", '_menu_item_menu_item_parent', $item->ID) ); – rockmandew Apr 04 '17 at 16:24
5

Since Wordpress 3.6.0 you can use the nav_menu_link_attributes filter:

add_filter( 'nav_menu_link_attributes', 'add_class_to_items_link', 10, 3 );

function add_class_to_items_link( $atts, $item, $args ) {
  // check if the item has children
  $hasChildren = (in_array('menu-item-has-children', $item->classes));
  if ($hasChildren) {
    // add the desired attributes:
    $atts['class'] = 'dropdown-toggle';
    $atts['data-toggle'] = 'dropdown';
    $atts['data-target'] = '#';
  }
  return $atts;
}

The tags unfortunately doesn't have a filter that we could use, so we'll need a walker:

class MY_Menu_Walker extends Walker_Nav_Menu {

  public function start_lvl( &$output, $depth = 0, $args = array() ) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"sub-menu dropdown-menu\">\n";
  }   

}

then add the walker option when calling the menu:

wp_nav_menu( array('walker' => new MY_Menu_Walker)); 
Victor BV
  • 1,051
  • 13
  • 9
3

Use This Code In Function.php

function add_menuclass($ulclass) {
   return preg_replace('/<a /', '<a class="list-group-item"', $ulclass);
}
add_filter('wp_nav_menu','add_menuclass');
Urooj Khan
  • 129
  • 1
  • 3
2

You can use this to check if there's a sub-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."'");

then do simple checks

if ( $has_children > 0 ) {
  // These lines adds your custom class and attribute
  $attributes .= ' class="dropdown-toggle"';
  $attributes .= ' data-toggle="dropdown"';
}

Remember to set $wpdb as global:

global $wp_query, $wpdb;

Tada~

Khairul
  • 840
  • 8
  • 14
1

You can place it into functions.php.

class My_Walker_Nav_Menu 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=\"dropdown-menu\">\n";
   }
  function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) 
  {
    global $wp_query, $wpdb;
    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
    $li_attributes = '';
    $class_names = $value = '';
    $classes = empty( $item->classes ) ? array() : (array) $item->classes;

    //Add class and attribute to LI element that contains a submenu UL.
    if ($args->has_children){
      $classes[]    = 'dropdown';
      $li_attributes .= 'data-dropdown="dropdown"';
    }
    $classes[] = 'menu-item-' . $item->ID;
    //If we are on the current page, add the active class to that menu item.
    $classes[] = ($item->current) ? 'active' : '';
    //Make sure you still add all of the WordPress classes.
    $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 ) . '"' : '';
    $has_children = $wpdb->get_var(
    $wpdb->prepare("
       SELECT COUNT(*) FROM $wpdb->postmeta
       WHERE meta_key = %s
       AND meta_value = %d
       ", '_menu_item_menu_item_parent', $item->ID)
     );
   $output .= $indent . '<li' . $id . $value . $class_names .'>';
   $output .= $indent . '<li' . $id . $value . $class_names . $li_attributes . '>';
   //Add attributes to link element.
   $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        ) .'"' : '';
   // Check if menu item is in main menu

if ( $depth == 0 && $has_children > 0  ) {
    // These lines adds your custom class and attribute
    $attributes .= ' class="dropdown-toggle"';
    $attributes .= ' 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;
   // Add the caret if menu level is 0
   if ( $depth == 0 && $has_children > 0  ) {
      $item_output .= ' <b class="caret"></b>';
   }
   $item_output .= '</a>';
   $item_output .= $args->after;
   $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
  }
}

Then in your template where you want to display this menu place this code.

   <?php

                if ( has_nav_menu( 'primary' ) ) {

                     $defaults = array(
                        'theme_location'  => 'primary',
                        //'menu'            => '',
                         'container'       => 'ul',
                         'container_class' => '',
                         'container_id'    => '',
                         'menu_class'      => '',
                         'menu_id'         => '',
                         'walker'          =>  new My_Walker_Nav_Menu()
                    );

                    wp_nav_menu( $defaults );

                }
            ?> 

Using this I solved my problem.

Lemon Kazi
  • 3,308
  • 2
  • 37
  • 67