Detailing the custom wordpress select menu walker

In this post I’ll go into detail about the changes made to the walker class in the Making select menu with wp_nav post. If you’re wondering how to put this to use in your theme make sure to read that post

The Walker

This is the same walker class as in the other post. It’s pretty similar to the default walker (located in wp-includes/nav-menu-template.php) with only a few changes. if you keep the default walker open while looking at this it might make it easier to see what changes were made.

This  custom class will modify 4 functions; function start_lvl(), function end_lvl(), function start_el() & end_el(). For the sake of simplicity I just dropped that custom class right into my functions.php file. The finished class is shown below; after the code snippet I’ll walk through the changes line by line. Note: you can also view the code on this gist page, it’s a little easier to read there.

|<- Expand ->|

class select_menu_walker extends Walker_Nav_Menu{
	 
	 function start_lvl(&$output, $depth) {
		$indent = str_repeat("t", $depth);
		$output .= "";
	}

	
	function end_lvl(&$output, $depth) {
		$indent = str_repeat("t", $depth);
		$output .= "";
	}
		 
	 function start_el(&$output, $item, $depth, $args) {
		global $wp_query;
		$indent = ( $depth ) ? str_repeat( "t", $depth ) : '';

		$class_names = $value = '';

		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
		$classes[] = 'menu-item-' . $item->ID;

		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
		$class_names = ' class="' . esc_attr( $class_names ) . '"';

		$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
		$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
		
		//check if current page is selected page and add selected value to select element  
		  $selc = '';
		  $curr_class = 'current-menu-item';
		  $is_current = strpos($class_names, $curr_class);
		  if($is_current === false){
	 		  $selc = "";
		  }else{
	 		  $selc = "selected ";
		  }
		
		$attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
		$attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
		$attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
		$attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
		
		$sel_val =  ' value="'   . esc_attr( $item->url        ) .'"';
		
		//check if the menu is a submenu
		switch ($depth){
		  case 0:
			   $dp = "";
			   break;
		  case 1:
			   $dp = "-";
			   break;
		  case 2:
			   $dp = "--";
			   break;
		  case 3:
			   $dp = "---";
			   break;
		  case 4:
			   $dp = "----";
			   break;
		  default:
			   $dp = "";
		}
		
		
		$output .= $indent . ''.$dp;

		$item_output = $args->before;
		//$item_output .= '';
		$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
		//$item_output .= '';
		$item_output .= $args->after;

		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
	}
	
	function end_el(&$output, $item, $depth) {
		$output .= "n";
	}
	
}

If you’re looking at the default walker you’re probably thinking this custom one looks very similar. It is in fact largely unchanged with only a few minor tweaks that I’ll explain below.

Remove Sub-menu UL’s

The first changes we made were to function start_lvl() and function end_lvl(), lines 5 and 11 in specific. By default this function inserts the submenu UL’s, you can check the original file to see how it looked. All we did was tell it to insert nothing. In your own code you could probably delete the entire function or even stop it from being called altogether, I left it in just so it’s easier to follow the changes.

Set current item

The next change is for setting the currently selected item, in other words, making sure the select menu displays the currently selected page.  This is done in lines 30-37. I first create the variable $selec, this will either be empty or set to ‘selected’. In lines 31 and 32 I look into the $class_names variable to check if the page we’re iterating over is the current page, if it is I set $selec to ‘selected’ and if it isn’t I leave $selec empty. Finally $selec is added to the menu’s output (you can see this on line 68).

Format sub-menus

After that, some code is added to insert dashes before the actual menu item based on the level of the menu. For example, items on the fist menu level have no dashes, the first sebmenu has a single dash, the next submenu has two dashes, etc. The end result is a menu that looks something like this;
Top level 1

-sub menu 1

–sub menu 2

Top level 2

-sub menu2

This is all done with the code between lines 47 and 65. I’m basically just using a switch statement to evaluate the variable $depth. The $depth variable simply stores a number that lets us now what level of submenu we’re currently on. This switch statement simply takes this and creates the variable $dp with the appropriate number of dashes. We then add this to the output by going back to line 68 and adding $dp.

Set value element

Next we need to set the value element. This is where we’ll store the URL that the menu will use for navigation. On line 44 we create a variable $sel_val and store the url item from the $item array. We also wrap it in the string value=”” since we will be adding this variable directly into the output (which again can be seen on line 68).

Change the LI tags

The last thing we need to do is change the tags. By default they are LI tags with the output wrapped in anchor tags. We obviously need OPTION tags and nothing wrapped around the output text. So first, on line 68, we change the LI tag to an option tag. Then we comment out lines 71 and 73 to get rid of the anchor tags (it’s assumed that you will simply delete these in your own implementation, I just commented them to show the differences). The last thing we need to do is go into the  end_el() function and on line 80 change the closing LI tag into the closing OPTION tag.

 

That’s all there is to it. With a few small tweaks to the walker we’ve manage to completely transform the output of wp_nav_menu(). One thing to note is that this walker needs to be used in conjunction with the correct $items_wrap value when you call wp_nav_menu. While you could forgo this by overwriting more of the default function, I wanted to use WordPress’ own functionality where possible. You can read more about the implementation of the menu is this post.