Auto-Generating a Select Menu in WordPress With a Custom Menu Walker

While working on the new website for BestAppSite, I wanted to have the functional <select> drop-down menu for smaller screen sizes, such as for the iPhone. These menus behave nicely and save valuable real-estate when you have many items to present.

Creating the menu itself isn’t very hard in plain HTML. The theory is to add the code for the drop-down underneath the default unordered list menu and then display/hide it using the media queries in the CSS. Couple that with the simple javascript snippet to open a URL from a drop-down selection, the menu itself is no problem.

Creating A Custom Menu Walker

By default, WordPress outputs a menu in an unordered list. To change this, you need to create a custom meny walker function. While its name makes your head spin, it is basically a PHP function that extends and thus overrides the default menu creation function.

Here is the code that I used for BestApp Site which is to be placed in your functions.php file.

/**
 * Custom Menu Walker for Responsive Menus
 *
 * Creates a <select> menu instead of the default
 * unordered list menus.
 *
 **/

class Walker_Responsive_Menu extends Walker_Nav_Menu {

	function start_el(&$output, $item, $depth, $args) {
		global $wp_query;		

		// Create a visual indent in the list if we have a child item.
		$visual_indent = ( $depth ) ? str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $depth) : '';

		// Load the item URL
		$attributes .= ! empty( $item->url ) ? ' value="'   . esc_attr( $item->url ) .'"' : '';

		$output .= '<form name="menu_selector" method="post" action="#">'; // Create the form wrapper for the URLs to work.
		$output .= '<select name="url_list" onchange="gotosite()">'; // Set up the select list and load the javascript function

		$output .= '<option value="" selected="selected" disabled="disabled">Please select...</option>'; // Create a default selected item.

		// If we have hierarchy for the item, add the indent, if not, leave it out.
		// Loop through and output each menu item as this.
		if($depth != 0) {
			$item_output .= '<option ' . $attributes .'>'. $visual_indent . apply_filters( 'the_title', $item->title, $item->ID ) . '</option>';
		} else {
			$item_output .= '<option ' . $attributes .'>'.$prepend.apply_filters( 'the_title', $item->title, $item->ID ).'</option>';
		}

		$output .= '</select>';
		$output .= '</form>';

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

While the code looks fairly complex, it is really straight forward. I have commented what each section does in the code rather than breaking it apart here.

Applying The Walker

In order to display the new menu, we need to call it using the wp_nav_menu function. At the place in your code where you want the menu to display, simply use this code:

<?php wp_nav_menu(array(
	'theme_location' => 'primary-menu',
	'container' => false,
	'walker' => new Walker_Responsive_Menu())
); ?>

This is a basic implementation of the wp_nav_menu function that adds the custom walker to use for the output You want to change the theme location parameter to the name of the theme location that you have set up in the theme.

Conclusion and Note

It is not necessarily very complex to make WordPress generate necessary code for responsive sites as this demonstrates, all because it is well-built from the start and allows you to override default function outputs with custom walkers.

Do note that this is a very simple and almost crude walker function as it omits much of the variables required for all the array parameters for the wp_nav_menu function to work. It will however do this job elegantly.

  • Matt

    Hey,

    Thanks so much for sharing this! I’ve been looking for a dropdown menu for wordpress (outside of a plugin) for a while

    One question though, whats making the menu jump to the top of the page?

    thanks,
    Matt

    • mabrahams123

      actually nevermind about my question, something else was doing that

    • http://www.xldstudios.com Erik Bernskiold

      I’m glad you were able to figure it out! :)

  • Flow

    $output . = should be $output .=
     

    • http://www.xldstudios.com Erik Bernskiold

      That is very correct. Thanks for catching the typo.

  • Guest

    Hi, this is exactly what I’ve been searching for, thanks! But I’m getting a lot of error messages, beginning with “Undefined property: stdClass::$menu_item_parent”. I just copied and pasted the code above and got the .js, is there something else I need? Thanks for any help.

  • Martin

    Hey,

    I’m looking for this exact thing but it doesn’t seem to work for me.

    It’s outputting several separate drop down select boxes with only the ‘Please select…’ option in each one and no other options.

    I’ve included the walker in my functions.php, used the code above to output the nav and I have the script you link to.

    Hope you can help,
    Martin

    • FGD

      Same here Martin, copy paste gets the same result…
      Any change you could help us out Erik?!

      • chirs

        same thing here. any one have any clue whats going on/.

  • http://www.ruturaaj.com/ Ruturaj

    This code is not working for me. It output several different drop-downs. I checked the previous comments and noticed that this issue is already reported. I checked “output .=” thing suggested in previous comments, but everything looks correct; still no result. Can you please tell me how to fix this issue?

    I’m using WordPress 3.5 version.

  • http://kingalca.de/ Max Alca

    Hey, ist it possible that PHP 5.4 doesnt support any lonter arrays like “start_el(&$output, $item, $depth, $args)” (with that “&”)? I think this is the problem why its not working correctly.

  • http://nativeimaging.com/ Native Imaging

    This is very close to what I’m attempting to accomplish with the wordpress menu walker.

    I’m trying to insert a element directly inside the menu container just before the menu…
    Would anyone be able to direct me to info to achieve this result?

    open menu

    menu item 1

    Bet regards! Thank You!