5

I'm adding a custom menu to my wordpress theme. I need to add custom html to the final submenu item - before the primary sub-menu closes </ul>

I have registered my new menu

//functions.php
function register_my_menus() {
register_nav_menus(
array(
  'accessories'  => __( 'Accessories Menu' )
)
);
}

and I have called my new menu in my header

//header.php
wp_nav_menu( array( 'theme_location' => 'accessories', 
                    'walker'   => BENZ_Walker_Nav_Menu_ACC                 
        ) ); 

I have attempted to modify a new Walker to add this div to my submenu, however this added the div for each menu item

// functions.php ==this adds one div foreach li==comment out
class BENZ_Walker_Nav_Menu_ACC extends Walker_Nav_Menu {

function start_lvl(&$output, $depth) {
    $output .= '<ul class="sub-menu">';
}
  function end_lvl(&$output, $depth) {
        //$output .='<div>the div that I want to show only once</div></ul>';
        $output .= '</ul>';
        }
    }       
}

Again the above example is commented out because it does not work for my needs

I have also tried to add append some html at the end of the menu with the following two eaxmples...

//functions.php == this shows custom html after the top-level `<li>`
function BENZ_menu_extras($menu, $args) {
if( 'accessories' !== $args->theme_location )
    return $menu;
return $menu . '<div>the div that I want to show only once</div>';
}
add_filter('wp_nav_menu_items','BENZ_menu_extras', 10, 2);

this is very close, however I do not need the html in the top level of hierarchy in the menu, but instead one level deeper.

I have also tried this example..

function add_last_nav_item($items, $args) {
if (!is_admin() && $args->theme_location == 'accessories') {
$items .= '<div>the div that I want to show only once</div>';
}
return $items;
}
add_filter( 'wp_nav_menu_items', 'add_last_nav_item', 10, 2 );

however this does the same as the first example listed...

How can we use either of the above functions with the $depth variable to add my div here....

<div class="accessories menu">
  <ul id="menu-accessories" class="menu sf-menu">
    <li id="menu-item-1905">
      <a class="sf-with-ul" href="http://#">ACCESSORIES</a>
      <ul class="sub-menu">
        <li id="menu-item-1897">
          <a class="sf-with-ul" href="http://#">stuff</a>
          <ul class="sub-menu">
            <li id="menu-item-1899">stuff1</li>
            <li id="menu-item-1898">stuff1</li>
          </ul>
        </li>
        <li id="menu-item-1903">
          <a class="sf-with-ul" href="http://#">other stuff</a>
          <ul class="sub-menu">
            <li id="menu-item-1906">other stuff1</li>
            <li id="menu-item-1907">other stuff2</li>
          </ul>
        </li>
        <li id="menu-item-1911">
          <a class="sf-with-ul" href="http://#">blue stuff</a>
          <ul class="sub-menu">
            <li id="menu-item-1912">blue stuff 1</li>
            <li id="menu-item-1913">blut stuff 2</li>
          </ul>
        </li>
    <!--This is where i want to put one div so its in the main submenu-->
    <div>the div that I want to show only once</div>
      </ul>
    </li>
  </ul>
</div>

https://jsfiddle.net/qpbpofpp/3/

Thanks for reading.

--BEGIN EDIT-- This is the closest I have gotten..

 //functions.php
 class BENZ_Walker_Nav_Menu_ACC extends Walker_Nav_Menu {
 function start_lvl(&$output, $depth) {
       $output .= '<ul class="sub-menu">';
}
function end_lvl(&$output, $depth) {
    if ($depth = 2){
        $output .= '<div>the div that I want to show only once</div></ul>';
    } else {
        $output .= '</ul>';
           }
    }    
}

except for now shows after all three of the sub menus, i just need it to show once

2 Answers 2

5

Ok, so it turns out that by default, $depth in the end_lvl() function starts at 0 when it's a submenu and increments as the depth gets greater. So we need to test if {$depth} is equal to 0 to apply it only to the first set of submenus:

function end_lvl( &$output, $depth, $args ) {
    if( 0 == $depth ) {
        $output .= '<div>the div that I want to show only once</div>';
    }

    $indent = str_repeat( "\t", $depth );
    $output .= "{$indent}</ul>\n";
}
0
5

The filter on wp_nav_menu_items should be the way to do this. Run the following (just hack into the theme header.php temporarily:

function add_last_nav_item($items, $args) {
  // if (!is_admin() && $args->theme_location == 'accessories') {
    $items .= '<div>the div that I want to show only once</div>';
  // }
  return $items;
}
add_filter( 'wp_nav_menu_items', 'add_last_nav_item', 10, 2 );

wp_nav_menu();

You should see exactly the effect that you describe. If you are seeing that code output at every level there is something wrong with, probably, your custom walker. If you look at the source, you will see that in Core that filter is applied once after the menu tree has been "walked". It should not be adding more than a single item to the end of the already existing items for the menu.

To control what level your code displays at, a modified walker would be the best approach:

class BENZ_Walker_Nav_Menu_ACC extends Walker_Nav_Menu {
  private $insert = true;
  function start_lvl(string &$output, int $depth) {
    $output .= '<ul class="sub-menu">';
  }
  function end_lvl(string &$output, int $depth) {
    if ($depth === 2 && $this->insert === true){
      $output .= '<div>the div that I want to show only once</div></ul>';
      $this->insert = false;
    } else {
      $output .= '</ul>';
    }
  }    
}

wp_nav_menu(array('walker' => new BENZ_Walker_Nav_Menu_ACC));

Notice the $insert class property. That is set to false on the first insertion to make sure that it only appears once.

3
  • I commented those lines to prove a point about the filter. Yes, in practise you'd need to limit it.
    – s_ha_dum
    Dec 16, 2015 at 17:56
  • @RobBenz : see the edit
    – s_ha_dum
    Dec 16, 2015 at 18:13
  • @s_ha_dum Thank you. this code worked for me. however I have gone with a different version, I cannot vote up your answer, but thank you.
    – RobBenz
    Dec 16, 2015 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.