title: Bootstrap Nav Walker - Add Class to Child UL - multi-level menu
body: <p>I'm working on a Wordpress site that someone else coded using Bootstrap. I added a submenu item to a submenu item, but it is appearing on top of it's parent, rather than under it.</p>
  
  <p>I found code that would add a class to the submenu, but I need to add a class to the child of the submenu item. </p>
  
  <p>Parent
    - A Submenu (child of main menu)
       --A1 Submenu (child of A)</p>
  
  <p>I created a dropdown-submenu class in the css file, but I don't know how or where to add it to the wp_bootstrap_navwalker.php file. Code is below.</p>
  
  <pre><code>&lt;?php
  
  /**
   * Class Name: wp_bootstrap_navwalker
   * GitHub URI: https://github.com/twittem/wp-bootstrap-
  
  navwalker
   * Description: A custom WordPress nav walker class to 
  
  implement the Bootstrap 3 navigation style in a custom theme 
  
  using the WordPress built in menu manager.
   * Version: 2.0.4
   * Author: Edward McIntyre - @twittem
   * License: GPL-2.0+
   * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
   */
  
  class wp_bootstrap_navwalker extends Walker_Nav_Menu {
  
      /**
       * @see Walker::start_lvl()
       * @since 3.0.0
       *
       * @param string $output Passed by reference. Used to 
  
  append additional content.
       * @param int $depth Depth of page. Used for padding.
       */
      public function start_lvl( &amp;$output, $depth = 0, $args = 
  
  array() ) {
          $indent = str_repeat( "\t", $depth );
          $output .= "\n$indent&lt;ul role=\"menu\" class=\" 
  
  dropdown-menu\"&gt;\n";
      }
  
      /**
       * @see Walker::start_el()
       * @since 3.0.0
       *
       * @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 int $current_page Menu item ID.
       * @param object $args
       */
      public function start_el( &amp;$output, $item, $depth = 0, 
  
  $args = array(), $id = 0 ) {
          $indent = ( $depth ) ? str_repeat( "\t", $depth ) : 
  
  '';
  
          /**
           * Dividers, Headers or Disabled
           * =============================
           * Determine whether the item is a Divider, Header, 
  
  Disabled or regular
           * menu item. To prevent errors we use the 
  
  strcasecmp() function to so a
           * comparison that is not case sensitive. The 
  
  strcasecmp() function returns
           * a 0 if the strings are equal.
           */
          if ( strcasecmp( $item-&gt;attr_title, 'divider' ) == 0 
  
  &amp;&amp; $depth === 1 ) {
              $output .= $indent . '&lt;li role="presentation" 
  
  class="divider"&gt;';
          } else if ( strcasecmp( $item-&gt;title, 'divider') == 0 
  
  &amp;&amp; $depth === 1 ) {
              $output .= $indent . '&lt;li role="presentation" 
  
  class="divider"&gt;';
          } else if ( strcasecmp( $item-&gt;attr_title, 
  
  'dropdown-header') == 0 &amp;&amp; $depth === 1 ) {
              $output .= $indent . '&lt;li role="presentation" 
  
  class="dropdown-header"&gt;' . esc_attr( $item-&gt;title );
          } else if ( strcasecmp($item-&gt;attr_title, 'disabled' 
  
  ) == 0 ) {
              $output .= $indent . '&lt;li role="presentation" 
  
  class="disabled"&gt;&lt;a href="#"&gt;' . esc_attr( $item-&gt;title ) . 
  
  '&lt;/a&gt;';
          } else {
  
              $class_names = $value = '';
  
              $classes = empty( $item-&gt;classes ) ? array() : 
  
  (array) $item-&gt;classes;
              $classes[] = 'menu-item-' . $item-&gt;ID;
  
              $class_names = join( ' ', apply_filters( 
  
  'nav_menu_css_class', array_filter( $classes ), $item, $args 
  
  ) );
  
              if ( $args-&gt;has_children )
                  $class_names .= ' dropdown';
  
              if ( in_array( 'current-menu-item', $classes ) )
                  $class_names .= ' active';
  
              $class_names = $class_names ? ' class="' . 
  
  esc_attr( $class_names ) . '"' : '';
  
              $id = apply_filters( 'nav_menu_item_id', 'menu-
  
  item-'. $item-&gt;ID, $item, $args );
              $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
  
              $output .= $indent . '&lt;li' . $id . $value . 
  
  $class_names .'&gt;';
  
              $atts = array();
              $atts['title']  = ! empty( $item-&gt;title )   ? 
  
  $item-&gt;title    : '';
              $atts['target'] = ! empty( $item-&gt;target )  ? 
  
  $item-&gt;target   : '';
              $atts['rel']    = ! empty( $item-&gt;xfn )     
  
  ? $item-&gt;xfn    : '';
  
              // If item has_children add atts to a.
              if ( $args-&gt;has_children &amp;&amp; $depth === 0 ) {
                  $atts['href'] = ! empty( $item-&gt;url ) ? 
  
  $item-&gt;url : '';
                  // $atts['data-toggle'] = 'dropdown';
                  $atts['class']          = 'dropdown-
  
  toggle';
                  $atts['aria-haspopup']  = 'true';
              } else {
                  $atts['href'] = ! empty( $item-&gt;url ) ? 
  
  $item-&gt;url : '';
              }
  
              $atts = apply_filters( 
  
  'nav_menu_link_attributes', $atts, $item, $args );
  
              $attributes = '';
              foreach ( $atts as $attr =&gt; $value ) {
                  if ( ! empty( $value ) ) {
                      $value = ( 'href' === $attr ) ? esc_url( 
  
  $value ) : esc_attr( $value );
                      $attributes .= ' ' . $attr . '="' . 
  
  $value . '"';
                  }
              }
  
              $item_output = $args-&gt;before;
  
              /*
               * Glyphicons
               * ===========
               * Since the the menu item is NOT a Divider or 
  
  Header we check the see
               * if there is a value in the attr_title 
  
  property. If the attr_title
               * property is NOT null we apply it as the class 
  
  name for the glyphicon.
               */
              if ( ! empty( $item-&gt;attr_title ) )
                  $item_output .= '&lt;a'. $attributes .'&gt;&lt;span 
  
  class="glyphicon ' . esc_attr( $item-&gt;attr_title ) . 
  
  '"&gt;&lt;/span&gt;&amp;nbsp;';
              else
                  $item_output .= '&lt;a'. $attributes .'&gt;';
  
              $item_output .= $args-&gt;link_before . 
  
  apply_filters( 'the_title', $item-&gt;title, $item-&gt;ID ) . 
  
  $args-&gt;link_after;
              $item_output .= ( $args-&gt;has_children &amp;&amp; 0 === 
  
  $depth ) ? ' &lt;span class="caret"&gt;&lt;/span&gt;&lt;/a&gt;' : '&lt;/a&gt;';
              $item_output .= $args-&gt;after;
  
              $output .= apply_filters( 
  
  'walker_nav_menu_start_el', $item_output, $item, $depth, 
  
  $args );
          }
      }
  
      /**
       * Traverse elements to create list from elements.
       *
       * Display one element if the element doesn't have any 
  
  children otherwise,
       * display the element and its children. Will only 
  
  traverse up to the max
       * depth and no ignore elements under that depth.
       *
       * This method shouldn't be called directly, use the 
  
  walk() method instead.
       *
       * @see Walker::start_el()
       * @since 2.5.0
       *
       * @param object $element Data object
       * @param array $children_elements List of elements to 
  
  continue traversing.
       * @param int $max_depth Max depth to traverse.
       * @param int $depth Depth of current element.
       * @param array $args
       * @param string $output Passed by reference. Used to 
  
  append additional content.
       * @return null Null on failure with no changes to 
  
  parameters.
       */
      public function display_element( $element, &amp;
  
  $children_elements, $max_depth, $depth, $args, &amp;$output ) {
          if ( ! $element )
              return;
  
          $id_field = $this-&gt;db_fields['id'];
  
          // Display this element.
          if ( is_object( $args[0] ) )
              $args[0]-&gt;has_children = ! empty( 
  
  $children_elements[ $element-&gt;$id_field ] );
  
          parent::display_element( $element, 
  
  $children_elements, $max_depth, $depth, $args, $output );
      }
  
      /**
       * Menu Fallback
       * =============
       * If this function is assigned to the wp_nav_menu's 
  
  fallback_cb variable
       * and a manu has not been assigned to the theme location 
  
  in the WordPress
       * menu manager the function with display nothing to a 
  
  non-logged in user,
       * and will add a link to the WordPress menu manager if 
  
  logged in as an admin.
       *
       * @param array $args passed from the wp_nav_menu 
  
  function.
       *
       */
      public static function fallback( $args ) {
          if ( current_user_can( 'manage_options' ) ) {
  
              extract( $args );
  
              $fb_output = null;
  
              if ( $container ) {
                  $fb_output = '&lt;' . $container;
  
                  if ( $container_id )
                      $fb_output .= ' id="' . $container_id . 
  
  '"';
  
                  if ( $container_class )
                      $fb_output .= ' class="' . 
  
  $container_class . '"';
  
                  $fb_output .= '&gt;';
              }
  
              $fb_output .= '&lt;ul';
  
              if ( $menu_id )
                  $fb_output .= ' id="' . $menu_id . '"';
  
              if ( $menu_class )
                  $fb_output .= ' class="' . $menu_class . '"';
  
              $fb_output .= '&gt;';
              $fb_output .= '&lt;li&gt;&lt;a href="' . admin_url( 'nav-
  
  menus.php' ) . '"&gt;Add a menu&lt;/a&gt;&lt;/li&gt;';
              $fb_output .= '&lt;/ul&gt;';
  
              if ( $container )
                  $fb_output .= '&lt;/' . $container . '&gt;';
  
              echo $fb_output;
          }
      }
  }
  </code></pre>