I am making my first custom walker to build an accordion menu. To start with I used this example: http://bitacre.com/2025/custom-nav-menu-walker-for-wordpress-themes

There are two functions. First start_lvl and then start_el.

In start_el the ID get's implemented by $item->ID. Does anybody know how I can do this in start_lvl as well? I need to give the (surrounding lower level navigation) an ID so I can trigger it to collapse in the accordion menu.

What I'm trying to generate is something like this:

<a href="#collapse2">Titel 2</a>
<ul id="collapse2">Lower Level Menu 2</ul>
<a href="#collapse3">Titel 3</a>
<ul id="collapse3">Lower Level Menu  3</ul>

My code for the start_lvl function:

// add id's and classes to ul sub-menus
function start_lvl( &$output, $depth, $item ) {
    // depth dependent classes
    $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
    $display_depth = ( $depth + 1); // because it counts the first submenu as 0
    $pgid = ; // How to get ID in here??
    $classes = array(
        ( $display_depth == 1  ? 'accordion-body collapse' : '' ),
        ( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
        ( $display_depth >=2 ? 'sub-sub-menu' : '' ),
        'menu-depth-' . $display_depth
    $ids = array(
        'collapse' . $pgid
    $class_names = implode( ' ', $classes );
    $id_name = implode( ' ', $ids );

    // build html
    $output .= "\n" . $indent . '<ul id="' . $id_name . '" class="' . $class_names . '">' . "\n";

I just had to do this in one of my themes... Since you don't have access to the $item variable at that stage of the Walker, you would want to store your current item in a more global scope at the moment you do have access to it. The following code will make more sense... note: I've stripped everything but the relevant code.

class ThemeTruck_Nav_Walker extends Walker_Nav_Menu {
   private $curItem;

  // retrieve the curItem
  function start_lvl(&$output, $depth = 0, $args = array()) {
    var_dump($this->curItem );

  // store the curItem
  function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
    $this->curItem = $item;

|improve this answer|||||
  • 3
    This is the most elegant solution presented in this thread. Thanks Lane! – Kevin C. May 31 '13 at 22:05
  • 1
    Indeed a very clean approach that works well. Many thanks. – Isaac Gregson Jun 1 '15 at 17:08
  • 1
    Im not sure but do i have to add the whole default code to start_el to make it work? – GDY Feb 7 '17 at 7:27
  • 4
    Ok you can just use parent::start_el($output,$item,$depth,$args,$id); in the start_el ... – GDY Feb 7 '17 at 7:37
  • Brilliant, been meaning to figure this out for ages and this worked right away. Thank you. – cfx Jun 21 '18 at 1:55

I had a similar problem and solved it by using a static variable inside the class:

static protected $menu_lvl; 

And then in the "display_element" I incremented the variable:


In my code I then referenced in the start_lvl function something like this:

$output .= "<ul id='level". self::$menu_lvl ."'>";

This doesn't use the page ID, but it does use a unique ID for UL statements that can be referenced by the javascript.

BTW - This is really just useful for nested accordions, or clickable nested dropdowns in the Roots Theme using Bootstrap for mobile apps.

|improve this answer|||||

You can use the following filter in your start_el function and grub your argument in start_lvl function.

apply_filters( 'walker_nav_menu_start_lvl', $item_output, $item, $depth, $args->myarg=$item->title );

Please let me know know if it works.

|improve this answer|||||
  • Thanks for posting! I hope to find some time to work it out in the next few days but I am really busy with my study just now. I will get back to you with the result! – Robert Bouten Sep 17 '12 at 20:09

You can simply add $page to the argument of the custom walker:

class My_Custom_Walker extends Walker_page {
    function start_el(&$output, $page, $depth, $args, $current_page) {
        if ( $depth )
            $indent = str_repeat("\t", $depth);
            $indent = '';

        extract($args, EXTR_SKIP);

        $output .= $indent . 
            <a style="color:red" href="' . get_page_link($page->ID) . '" title="' . 
            esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . 
            $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';

Try the above and then before you call wp_list_pages(), add the custom walker class:

$MyWalker = new My_Custom_Walker();

Then, in the arguments for wp_list_pages:

wp_list_pages('walker' => $MyWalker)

Check and see if the output of the walker is red.

|improve this answer|||||
  • That is in function start_el, but it's apparently different in start_lvl because I can't put the same variables in there. Or at least not the same order. – Robert Bouten Aug 16 '12 at 20:38
  • What are you trying to do with start_lvl? – AlxVallejo Aug 16 '12 at 20:40
  • I am trying to give the <ul> an id="collapse102" with 102 the generated page ID. This way I can trigger it to collapse it in my accordion menu. – Robert Bouten Aug 16 '12 at 20:50
  • Adding the id="collapse" works, but I can't get it to work to add the pageID as well. – Robert Bouten Aug 16 '12 at 20:51
  • Do you mean that you want only specific IDs to be collapsed? I have an accordion tree for wp_list_pages and I don't need to alter start_lvl at all. – AlxVallejo Aug 16 '12 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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