Creating Dynamic Select Menus in WordPress for Mobile Device Navigation

This tutorial was updated on 02/01/2012 to include children items in the select menu. Go to Update

We are in the process of updating Mobile (a WordPress plugin for applying a custom theme to mobile devices) and we were discussing how to go about displaying menus. Currently Mobile registers a new menu and uses Javascript to add a “drop-down” to the front end.

Let’s face it. Navigating on mobile devices can sometimes be such a hassle. Small buttons and big fingers simply do not mix.

Thankfully the HTML select tag is widely used as an easy way to provide a navigation for mobile devices, specifically the iPhone. While this is beneficial, how are we supposed to use this tag while still using the default WordPress menu system?

Quite easily actually, but be warned that this tutorial is quire lengthy and descriptive.

Register the Menu Location

This part is the most important. Before we can even begin, we need a menu to work with and this function will allow us to register this menu. For more information about this function, read this.

Get started by opening up your functions.php file and adding the this code:

register_nav_menus(
	array(
		'select-menu' => 'Select Menu',
	)
);

You can change key (select-menu) and description (Select Menu) to what ever you choose. I choose this key and description merely for demonstration purposes.

Create a New Menu Output Function

After we register our menu, we need to create a new output function that allows us to call the menu within a template file. This function can also be placed in the functions.php file.

Here is a sample function:

function wp_nav_menu_select( $args = array() ) {

	$defaults = array(
		'theme_location' => '',
		'menu_class' => 'select-menu',
	);

	$args = wp_parse_args( $args, $defaults );

	if ( ( $menu_locations = get_nav_menu_locations() ) && isset( $menu_locations[ $args['theme_location'] ] ) ) {
		$menu = wp_get_nav_menu_object( $menu_locations[ $args['theme_location'] ] );

		$menu_items = wp_get_nav_menu_items( $menu->term_id );
		?>
			<select id="menu-<?php echo $args['theme_location'] ?>" class="<?php echo $args['menu_class'] ?>">
				<option value=""><?php _e( 'Navigation' ); ?></option>
				<?php foreach( (array) $menu_items as $key => $menu_item ) : ?>
					<option value="<?php echo $menu_item->url ?>"><?php echo $menu_item->title ?></option>
				<?php endforeach; ?>
			</select>
		<?php
	}

	else {
		?>
			<select class="menu-not-found">
				<option value=""><?php _e( 'Menu Not Found' ); ?></option>
			</option>
		<?php
	}

}

I would like to note that the name of the function here can be anything you liked and is not to be confused with the WordPress core function wp_nav_menu. I merely used this function name because many people are already familiar with that function and how to use it.

Function Explained

The first part of the function (below) sets up some $defaults and merges those with the $args. When calling the function later, we will use these variables to define which menu and what class name we will use (the class name select-menu is used by default).

$defaults = array(
	'theme_location' => '',
	'menu_class' => 'select-menu',
);

$args = wp_parse_args( $args, $defaults );

The second part of the function is the actual output. It first finds out if the a menu is in the correct theme location and (if so) grabs that specific menu’s items and outputs them as a select with it’s various options. If it does not exist, the output will merely be a select with one option: Menu Not Found.

if ( ( $menu_locations = get_nav_menu_locations() ) && isset( $menu_locations[ $args['theme_location'] ] ) ) {
	$menu = wp_get_nav_menu_object( $menu_locations[ $args['theme_location'] ] );

	$menu_items = wp_get_nav_menu_items( $menu->term_id );
	?>
		<select id="menu-<?php echo $args['theme_location'] ?>" class="<?php echo $args['menu_class'] ?>">
			<option value=""><?php _e( 'Navigation' ); ?></option>
			<?php foreach( (array) $menu_items as $key => $menu_item ) : ?>
				<option value="<?php echo $menu_item->url ?>"><?php echo $menu_item->title ?></option>
			<?php endforeach; ?>
		</select>
	<?php
}

else {
	?>
		<select class="menu-not-found">
			<option value=""><?php _e( 'Menu Not Found' ); ?></option>
		</select>
	<?php
}

Call the New Menu

Finally we will need to call the new menu within our theme. Remember the select-menu key we used to register our menu? We will use that to define which menu to use.

Open up a template file (header.php, footer.php, whatever.php) and call the menu like so:

wp_nav_menu_select(
	array(
		'theme_location' => 'select-menu'
	)
);

Alternatively you could also change the class name of the menu, but if you do you will need to alter the jQuery (explained below) to work with the new class. Here’s how you would change the class:

wp_nav_menu_select(
	array(
		'theme_location' => 'select-menu',
		'menu_class' => 'custom-class'
	)
);

Making it Work with jQuery

Out of the box this select tag will do nothing at all. We will need some jQuery (and the jQuery library) to make it work.

First we will need to create a file within our template (I prefer to keep all my javascript files in a js folder). For now we will call this file select-menu.js.

Open that file and put the following code:

jQuery(document).ready(function() {
	jQuery( ".select-menu" ).change(function() {
		window.location = jQuery(this).find("option:selected").val();
	});
});

Remember that ability to change the class that I mentioned when calling at the new navigation menu? If you choose to change the menu, you will need to change the class that jQuery looks for above. Note that there is no need to change the class (even if you have multiple menus on one page).

Next we need to correctly enqueue our scripts within our theme. All of this can be accomplished by the following code placed within our functions.php (right under out wp_nav_menu_select function if you prefer).

function wp_nav_menu_select_scripts() {
	wp_enqueue_script( 'select-menu', get_bloginfo( 'stylesheet_directory' ) . '/js/select-menu.js', array('jquery'), '', true );
}
add_action( 'wp_enqueue_scripts', 'wp_nav_menu_select_scripts' );

Don’t know what this function is doing? That’s okay, you can learn about it here.

Wrapping it Up

So, now we should have a new menu within our theme that uses the select HTML tag instead of the default list-items. You can use the WordPress menu system to control all the items that are displayed within your menu for mobile devices.

You may noticed that this menu always displays, but you can control that with some fancy CSS @media queries, or create an entirely different theme for mobile devices with PluginBuddy’s fancy plugin: Mobile.

Say Thanks by Donating to this Plugin

Update: 02/01/2012

Lots of people requested advice on how to add children to the parent items in the menu (see comments). I was able to recreate the output to add the children to their parents; however, this will only work with children and not grandchildren. The problem is not the output, but the fact that selects do not allow nested option groups (optgroup) at this time (let’s hope this changes).

Anyway, here is what I have come up with:

 '',
		'menu_class' => 'select-menu',
	);
	
    $args = wp_parse_args( $args, $defaults );
	 
	if ( ( $menu_locations = get_nav_menu_locations() ) && isset( $menu_locations[ $args['theme_location'] ] ) ) {
		$menu = wp_get_nav_menu_object( $menu_locations[ $args['theme_location'] ] );
		 
		$menu_items = wp_get_nav_menu_items( $menu->term_id );
		
		$children = array();
		$parents = array();
		
		foreach ( $menu_items as $id => $data ) {
			if ( empty( $data->menu_item_parent )  ) {
				$top_level[$data->ID] = $data;
			} else {
				$children[$data->menu_item_parent][$data->ID] = $data;
			}
		}
		
		foreach ( $top_level as $id => $data ) {
			foreach ( $children as $parent => $items ) {
				if ( $id == $parent  ) {
					$menu_item[$id] = array(
						'parent' => true,
						'item' => $data,
						'children' => $items,
					);
					$parents[] = $parent;
				}
			}
		}
		
		foreach ( $top_level as $id => $data ) {
			if ( ! in_array( $id, $parents ) ) {
				$menu_item[$id] = array(
					'parent' => false,
					'item' => $data,
				);
			}
		}
		
		uksort( $menu_item, 'wp_nav_menu_select_sort' ); 
		
		?>
			
		
			
		

Simply replace the previous wp_nav_menu_select function with both the above functions. Let me know how this works out!

30 Comments

  1. Ankit Kumar · April 17

    Hey Justin!

    Your Site is really great. Can you put some tuts for theme development like yours.

    Happy New Year !!

  2. Laura · April 17, 2012

    How would you add a second one of these in?

    • Justin Kopepasah · April 17, 2012

      To add additional menus you would simply call the wp_nav_menu_select() function with the appropriate arguments in the array.

  3. Kenny · April 17, 2012

    Hey there
    finally found a guide i could use. How ever
    it shows “Menu not found” no matter what i try with it…
    could you perhaps have it looked over and update it please or tell me what i do wrong

    • Justin Kopepasah · April 17, 2012

      Looked over the code and everything works for me. Not sure of the problem without looking at the source.

  4. K'reen · April 17, 2012

    Hi Justin,

    First of all thank you for this great tutorial. That’s very useful.
    I have a question. Is it possible to keep the select choice?
    For example, if I click on “about me”, the menu shows “about me” and not the default menu name “navigation”.

    Thanx for your precious help

    • Justin Kopepasah · April 17, 2012

      The easiest way to do this, without getting into PHP, is using jQuery.

      Add this code to the jQuery above.

      jQuery('.select-menu option[value=&quot;' + document.URL + '&quot;]').attr('selected','selected');
      
      • K-reen · April 17, 2012

        Hi Justin
        Thank you for your answer. Not sure about how to implement this new line.
        Like this :

        jQuery( “.select-menu” ).change(function() {
        window.location = jQuery(this).find(“option:selected”).val();
        jQuery(‘.select-menu option[value=”‘ + document.URL + ‘”]’).attr(‘selected’,’selected’);
        });

        And I have a other question for you. Have you planned to do the example with children of children ?

        Have a good day and thank you again

        • Justin Kopepasah · April 17, 2012

          You would add that code to a Javascript file.

          I have thought about doing a post, but not sure of the timeline.

  5. pascal · April 17, 2012

    Hello,
    I tried your method, I’ve got a select menu dropdown with my original menu items, but, the links didn’t worked.

    In my header.php :

    	 'Menu Principal' )); ?>
    	 'main_nav')); ?>			      
    

    In my functions.php :

    	if (function_exists('register_nav_menus')) {
    		register_nav_menus(
    			array(
    				'main_nav' => 'Menu Principal',
    			)
    		);
    	}
    
    
    function wp_nav_menu_select_sort( $a, $b ) {
        return $a = $b;
    }
     
    function wp_nav_menu_select( $args = array() ) {
         
        $defaults = array(
            'theme_location' => '',
            'menu_class' => 'main_nav',
        );
    .../... the rest of your code.
    

    Where did you think I’m wrong?

    Regards
    Pascal

    • Justin Kopepasah · April 17, 2012

      My guess is that you did not add necessary Javascript. You should see it in the tutorial above.

  6. Patrick · April 17, 2012

    Thank you so much for this! I have been searching for hours for this solution. I’m so glad to have found this one. Works great! Also, I love your site design! Thanks again, Justin!

  7. Tobi · April 17, 2012

    Hey! WOW, danke für das simple tutorial. Genau das hab ich gesucht und hat auf anhieb geklappt. Das spart mir ordentlich Zeit.

    Translated: Hey! WOW, thanks for the simple tutorial. Exactly what I wanted and worked right away. It saves me time.

    Tobi

  8. luis · April 17, 2012

    Hi, im a newbie in wordpress programming,so i was wondering if do you have a live example from the menú in any wordpress page actually running? I just want it to see how this works.

    Thanks

  9. Stefan · April 17, 2012

    Nice script! its working fine for me, only…
    How can you get the menu order to be the same as in de admin panel?

    • Justin Kopepasah · April 17, 2012

      The order should be the same order as the menu in the WordPress admin. Did you use the updated function at the bottom of the post?

  10. Justin Kopepasah · April 17, 2012

    Just wanted you all to know I posted an update that solved the problem of children in the menus.

  11. Justin Kopepasah · April 17, 2012

    Sorry for the neglected response of comments. I will work on a solution that handles this children and post an update soon.

  12. Alin · April 17, 2012

    made it work thanks for the great tutorial

  13. Alin · April 17, 2012

    Hi, I did everything I use jquery mobile I get the menu and in option value the link I also did add in function under the wp_nav_menu_select the jquery but still it does not send me when I select on the page , can you help me ?

  14. Claudio Rimann · April 17, 2012

    Nice, thanks a lot for the tutorial!

    Do you think it would be possible to somehow visually separate parent / child menu-items inside the select tag?

    For example inserting a “–” before Child items like this:

    Parent
    – Child
    – Child
    –– Child of Child
    Parent
    etc…

  15. Diego · April 17, 2012

    Thanks for the tutorial, it worked perfect for me. But I have a question, how can I differentiate the Menu from The Submenu?
    For Example to appear something like this in the select:
    Main Page
    – Subpage
    – Subpage 2
    Main Page 2
    – Subpage
    – Subpage 2
    – Subpage 3
    Main Page 3

    Thanks Again

  16. Frederik vdb · April 17, 2012

    Great tutorial, altough it took some time to get it working here..

    Is it possible to make multiple dropdowns except for just copying all the code multiple times?

  17. Kryvulena · April 17, 2012

    I also get a select menu rendered saying “No Menu Found” – this is the only item at the select menu list. In addition, all page content below is gone.

    I have registered the menu in functions.php, js file loaded etc.

  18. daniele · April 17, 2012

    Hey i’ve implemented this and it works great, but if i wanted to show the right page instead of a simple navigation how can i do?
    I mean, if i click on home when the page refresh i wanna show home in my menu, when i click on contact i wanna show contact, not the standard word “navigation”.
    Any clue?
    Thanks a lot already it was very useful

  19. jose luis · April 17, 2012

    Hi, I have implemented this but doesn´t work. It gives me no menu found. Do I have to change the “theme_location” to my theme folder name?