Customizing Tree-Like Data Structures In WordPress With The Walker Class

About The Author

Carlo is a freelance front-end designer and developer. In the last years he’s been writing for ioProgrammo, an Italian printed computer magazine, and … More about Carlo ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Starting with version 2.1, WordPress provides the Walker abstract class, with the specific function of traversing these tree-like data structures. But an abstract class does not produce any output by itself. It has to be extended with a concrete child class that builds the HTML bricks for specific lists of items. In this article, Carlo Daniele will explore some of the most common uses of the Walker class. Note, however, that the following examples do not cover all possible applications and alternative ways to take advantage of the class. But you’ll discover more just by making use of your imagination and your skills as a programmer.

In WordPress, a navigation menu, a list of categories or pages, and a list of comments all share one common characteristic: They are the visual representation of tree-like data structures. This means that a relationship of superordination and subordination exists among the elements of each data tree.

There will be elements that are parents of other elements and, conversely, elements that are children of other elements. A reply to a comment depends logically on its parent, in the same way that a submenu item depends logically on the root element of the tree (or subtree).

Starting with version 2.1, WordPress provides the Walker abstract class, with the specific function of traversing these tree-like data structures. But an abstract class does not produce any output by itself. It has to be extended with a concrete child class that builds the HTML bricks for specific lists of items. With this precise function, WordPress provides the Walker_Category class to produce a nested list of categories, the Walker_Page class, which builds a list of pages, and several other Walker concrete child classes.

But when we build a WordPress theme, we might need to customize the HTML list’s default structure to fit our particular needs. We may want to add a description to a menu item, add custom class names or perhaps redefine the HTML structure of a list of categories or comments.

Before we begin, let’s look at the most important concept in this article.

Tree-Like Data Structures

Wikipedia defines a tree as a hierarchical organization of data:

“A tree data structure can be defined recursively (locally) as a collection of nodes (starting at a root node), where each node is a data structure consisting of a value, together with a list of references to nodes (the ‘children’), with the constraints that no reference is duplicated, and none points to the root.”

So, the structure is characterized by a root element (at the first level), parent elements (which are directly referenced to subordered elements, named children), siblings (which are elements placed at the same hierarchical level) and descendant and ancestor elements (which are connected by more than one parent-child relation).

The wp_comments table structure
Each element in the wp_comments is referenced to its parent by the value of the comment_parent field. (View large version)

As an example of this kind of structure, let’s take the wp_comments table from the WordPress database. The comment_parent field stores the parent ID of each element in the structure, making it possible to create the reference from the child node to its parent.

What We’ll Be Doing

Before moving forward, I’ll show you a simple example of a concrete child class producing the HTML markup of a category list.

The category list in WordPress is printed out by the wp_list_categories template tag. When we call this function, it executes the Walker_Category class, which actually builds the HTML structure, producing something like the following code:

<li class="categories">Categories
   <ul>  
      <li class="cat-item cat-item-10">
         <a href="https://example.com/wordpress/category/coding/">Coding</a>
         <ul class="children">
            <li class="cat-item cat-item-39">
               <a href="https://example.com/wordpress/category/coding/php/">PHP</a>
            </li>
         </ul>
      </li>
      <li class="cat-item cat-item-11">
         <a href="https://example.com/wordpress/category/design/">Design</a>
      </li>
   </ul>
</li>

wp_list_categories will print the output produced by the Walker_Category concrete class. It will be a wrapper list item holding a nested list of categories.

Now, suppose you don’t like this kind of list, and you want to print custom HTML code. You can do this trick by defining your own Walker extention. In your theme’s functions.php file, add the following code:

class My_Custom_Walker extends Walker
{
   public $tree_type = 'category';

   public $db_fields = array ('parent' => 'parent', 'id' => 'term_id');

   public function start_lvl( &$output, $depth = 0, $args = array() ) {
      $output .= "<ul class='children'>\n";
   }

   public function end_lvl( &$output, $depth = 0, $args = array() ) {
      $output .= "</ul>\n";
   }

   public function start_el( &$output, $category, $depth = 0, $args = array(), $current_object_id = 0 ) {
      $output .= "<li>" . $category->name . "\n";
   }

   public function end_el( &$output, $category, $depth = 0, $args = array() ) {
      $output .= "</li>\n";
   }
}

Even if you don’t know a lot about PHP classes, the code is quite descriptive. The start_lvl() method prints the start tag for each level of the tree (usually a <ul> tag), while end_lvl() prints the end of each level. In the same way, the start_el and end_el methods open and close each item in the list.

This is just a basic example, and we won’t dive deep into the Walker properties and methods for now. I’ll just say that the concrete child class My_Custom_Walker extends the abstract class Walker, redefining some of its properties and methods.

As I wrote above, the category list is printed out by the wp_list_categories template tag, but the HTML structure is built by the Walker_Category class. WordPress allows us to pass a custom walker class to the template tag. The new walker will build a custom HTML structure that will be printed on the screen by wp_list_categories.

As a first example, let’s create a shortcode that will print new markup for the lists of categories. In your functions.php file, add the following code:

function my_init() {
   add_shortcode( 'list', 'my_list' );
}
add_action('init', 'my_init');

function my_list( $atts ){
   $list = wp_list_categories( array( 'echo' => 0, 'walker' => new My_Custom_Walker() ) );
   return $list;
}

We are passing the function an array of two arguments: echo keeps the result in a variable, and walker sets a custom Walker (or Walker_Category) child class.

Finally, the shortcode [list] will print the resulting HTML code:

<ul>
   <li class="categories">Categories
      <ul>
         <li>Coding
            <ul class="children">
            <li>PHP</li>
            </ul>
         </li>
         <li>Design</li>
      </ul>
   </li>
</ul>

As you can see, the example is very basic but should give you an idea of our goals.

The Walker Class And Its Extensions

The Walker class is defined in wp-includes/class-wp-walker.php as “a class for displaying various tree-like structures.”

As I mentioned before, it’s an abstract class and does not produce any output by itself; rather, it has to be extended by defining one or more concrete child classes. Only these concrete child classes will produce the HTML markup. WordPress provides several extensions of the Walker class, each one producing a hierarchical HTML structure.

Look at the table below:


Class nameDefined inExtends
Walker_Pagepost-template.phpWalker
Walker_PageDropdownpost-template.phpWalker
Walker_Categorycategory-template.phpWalker
Walker_CategoryDropdowncategory-template.phpWalker
Walker_Category_Checklisttemplate.phpWalker
Walker_Commentcomment-template.phpWalker
Walker_Nav_Menunav-menu-template.phpWalker
Walker_Nav_Menu_Checklistnav-menu.phpWalker_Nav_Menu
Walker_Nav_Menu_Editnav-menu.phpWalker_Nav_Menu

This table shows the built-in Walker child classes, the template files they are defined in, and the corresponding parent class.

The goal of this article is to put the Walker class to work, so we need to dive into its structure.

The Walker Class’ Structure

Walker is defined in wp-includes/class-wp-walker.php. It declares four properties and six main methods, most of which are left empty and should be redefined by the concrete child classes.

The properties are:

  • $tree_type
  • $db_fields
  • $max_pages
  • $has_children

$tree_type

This is a string or an array of the data handled by the Walker class and its extentions.

public $tree_type;

The Walker class declares the property but does not set its value. To be used, it has to be redeclared by the concrete child class. For example, the Walker_Page class declares the following $tree_type property:

public $tree_type = 'page';

Whereas Walker_Nav_menu declares the following $tree_type:

public $tree_type = array( 'post_type', 'taxonomy', 'custom' );

$db_fields

Just like $tree_type, $db_fields is declared with no value assigned. In the Walker class’ documentation, it just says that its value is an array:

public $db_fields;

The elements of the array should set the database fields providing the ID and the parent ID for each item of the traversed data set.

The Walker_Nav_Menu class redeclares $db_fields as follows:

public $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

$db_fields[‘parent’] and $db_fields[‘id’] will be used by the display_element method.

$max_pages

This keeps in memory the maximum number of pages traversed by the paged_walker method. Its initial value is set to 1.

public $max_pages = 1;

$has_children

This boolean is set to true if the current element has children.

public $has_children;

After the properties, the methods are:

  • start_lvl
  • end_lvl
  • start_el
  • end_el
  • display_element
  • walk

start_lvl

This method is executed when the Walker class reaches the root level of a new subtree. Usually, it is the container for subtree elements.

public function start_lvl( &$output, $depth = 0, $args = array() ) {}

start_lvl() takes three arguments:


ArgumentTypeDescription
$outputstringpassed by reference; used to append additional content
$depthintdepth of the item
$argsarrayan array of additional arguments

Because it produces HTML output, start_lvl should be redefined when extending the Walker class. For instance, the start_lvl method of the Walker_Nav_Menu class will output a ul element and is defined as follows:

public function start_lvl( &$output, $depth = 0, $args = array() ) {
   $indent = str_repeat("\t", $depth);
   $output .= "\n$indent<ul class=\"sub-menu\">\n";
}

end_lvl

The second method of the Walker class closes the tag previously opened by start_lvl.

public function end_lvl( &$output, $depth = 0, $args = array() ) {}

The end_lvl method of Walker_Nav_Menu closes the unordered list:

public function end_lvl( &$output, $depth = 0, $args = array() ) {
   $indent = str_repeat("\t", $depth);
   $output .= "$indent</ul>\n";
}

start_el

This method opens the tag corresponding to each element of the tree. Obviously, if start_lvl opens a ul element, then start_el must open an li element. The Walker class defines start_el as follows:

public function start_el( &$output, $object, $depth = 0, $args = array(), $id = 0 ) {}
ArgumentTypeDescription
$outputstringpassed by reference; used to append additional content
$objectobjectthe data object
$depthintdepth of the item
$argsarrayan array of additional arguments
$idintcurrent item ID

end_el

It closes the tag opened by start_el.

public function end_el( &$output, $object, $depth = 0, $args = array() ) {}

Finally, we’ve reached the core of the Walker class. The following two methods are used to iterare over the elements of the arrays of objects retrieved from the database.

display_element

I won’t show the full code here, because you can find it in the WordPress Trac. I’ll just say that this method displays the elements of the tree.

public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {}

display_element takes the following arguments:


ArgumentTypeDescription
$elementobjectthe data object
$children_elementsarraylist of elements to continue traversing
$max_depthintmaximum depth to traverse
$depthintdepth of current element
$argsarrayan array of arguments
$outputstringpassed by reference; used to append additional content

This method does not output HTML on its own. The markup will be built by a call to each of the previously described methods: start_lvl, end_lvl, start_el and end_el.

walk

walk is the core of the Walker class. It iterates over the elements of the tree depending on the value of $max_depth argument (see the Trac for the full code).

public function walk( $elements, $max_depth ) {}

walk takes two arguments.


ArgumentTypeDescription
$elementsarrayan array of elements
$max_depthintmaximum depth to traverse

Other methods are used for more specific pourposes.

paged_walk

This builds a page of nested elements. The method establishes which elements of the data tree should belong to a page, and then it builds the markup by calling display_element and outputs the result.

public function paged_walk( $elements, $max_depth, $page_num, $per_page ) {}

get_number_of_root_elements

This gets the number of first-level elements.

public function get_number_of_root_elements( $elements ){}

unset_children

This last method unsets the array of child elements for a given root element.

public function unset_children( $e, &$children_elements ){}

Now that we’ve introduced the Walker properties and methods, we can explore one of its possible applications: changing the HTML structure of the navigation menu. The default markup for navigation menus is produced by the Walker_Nav_Menu concrete class. For this reason, we’ll create a new concrete Walker child class based on Walker_Nav_Menu, and we’ll pass it to wp_nav_menu to replace the output.

But is extending the Walker_Nav_Menu class always necessary when rebuilding a menu? Of course not!

Most of the time, a call to the wp_nav_menu template tag will suffice.

Menus admin page
In the “Menus” editing page, setting theme locations for each custom menu is possible.

Basic Customization Of The Navigation Menu: The wp_nav_menu Template Tag

The navigation menu can be included in the theme’s template files with a call to the wp_nav_menu() template tag. This function takes just one argument, an array of parameters that is well described in the Codex.

wp_nav_menu() can be included in your templates as follows:

$defaults = array(
   'theme_location'     => ’,
   'menu'            => ’,
   'container'       => 'div',
   'container_class' => ’,
   'container_id'    => ’,
   'menu_class'         => 'menu',
   'menu_id'         => ’,
   'echo'            => true,
   'fallback_cb'     => 'wp_page_menu',
   'before'          => ’,
   'after'           => ’,
   'link_before'     => ’,
   'link_after'         => ’,
   'items_wrap'         => '<ul id="%1$s" class="%2$s">%3$s</ul>',
   'depth'           => 0,
   'walker'          => ’
);

wp_nav_menu( $defaults );

WordPress provides many parameters to configure the navigation menu. We can change the menu’s container (it defaults to a div), the container’s CSS class and ID, as well as the text strings and markup to be included before and after the anchor element and before and after the item’s title. Furthermore, we can change the root element’s structure (items_wrap) and the depht of the tree.

So, we don’t need to extend the Walker (or the Walker_Nav_Menu) class each time we want to make changes to the menu’s structure — only when we have to produce more advanced structural customizations. This happens when we have to assign CSS classes to the menu elements when a specific condition occurs, or when we have to add data or HTML code to the menu items.

This can be done by setting a value for the walker parameter, which will be nothing but an instance of a custom concrete class. So, from now on, we’ll dive more and more deeply into WordPress menus, from changing the menu structure to adding custom fields to the menu items’ editing boxes. As I said, we’ll do this job by extending the Walker_Nav_Menu, so it’s time to get acquainted with it.

The Walker_Nav_Menu Class

The Walker_Nav_Menu class is defined in wp-includes/nav-menu-template.php. This is the class used by WordPress to build the navigation menu’s structure. Each time you want to make relevant changes to the menu’s default structure, you can extend Walker_Nav_Menu.

The concrete class redeclares the $tree_type and $db_fields properties and the start_lvl, end_lvl, start_el and end_el methods.

class Walker_Nav_Menu extends Walker {

   public $tree_type = array( 'post_type', 'taxonomy', 'custom' );

   public $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

   public function start_lvl( &$output, $depth = 0, $args = array() ) {
      $indent = str_repeat("\t", $depth);
      $output .= "\n$indent<ul class=\"sub-menu\">\n";
   }

   public function end_lvl( &$output, $depth = 0, $args = array() ) {
      $indent = str_repeat("\t", $depth);
      $output .= "$indent</ul>\n";
   }

   public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
      // code below
   }

   public function end_el( &$output, $item, $depth = 0, $args = array() ) {
      $output .= "</li>\n";
   }

} // Walker_Nav_Menu

The start_el public method builds the HTML markup of the opening li tag for each menu item. It is defined as follows:

public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
   $indent = ( $depth ) ? str_repeat( "\t", $depth ) : ’;

   $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, $depth ) );
   $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : ’;

   $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth );
   $id = $id ? ' id="' . esc_attr( $id ) . '"' : ’;

   $output .= $indent . '<li' . $id . $class_names .'>';

   $atts = array();
   $atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : ’;
   $atts['target'] = ! empty( $item->target )     ? $item->target     : ’;
   $atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : ’;
   $atts['href']   = ! empty( $item->url )        ? $item->url        : ’;

   $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

   $attributes = ’;
   foreach ( $atts as $attr => $value ) {
      if ( ! empty( $value ) ) {
         $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
         $attributes .= ' ' . $attr . '="' . $value . '"';
      }
   }

   $item_output = $args->before;
   $item_output .= '<a'. $attributes .'>';

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

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

The code is quite self-explanatory:

  • $indent stores a number of '/t' strings corresponding to $depth’s value;
  • $classes is an array of the CSS classes assigned to the list item;
  • $id records the element’s ID, composed by the prefix 'menu-item-' and the item’s ID retrieved from the database;
  • $atts is an array of the element’s attributes;
  • $item_output keeps track of the HTML code before it is assigned to $output;
  • $output stores the HTML markup.

Extending The Walker_Nav_Menu Class

When building your custom navigation menu markup, you might decide to extend the Walker class itself or its concrete child class Walker_Nav_Menu. If you opt to extend the Walker class, you’ll need to define all necessary properties and methods. Otherwise, if you choose to extend the concrete child class, you’ll just need to define those methods whose output has to be changed.

The following example describes a real-life situation in which the default menu’s structure has to be changed in order to integrate a WordPress theme with the Foundation 5 framework.

Foundation 5 home page
Foundation 5’s home page.

A Working Example: The Foundation Top Bar As WordPress Navigation Menu

Foundation 5 comes with a flexible grid system and with plugins and components that make it easy to develop solid and responsive websites. So, chances are that you’ll decide to enrich your theme with all of this great stuff.

First, you need to include all necessary scripts and style sheets in your theme. This isn’t our topic, so we won’t dive deep into the configuration, but you can pull out all necessary information directly from Foundation’s documentation and the WordPress Codex.

Here, we’ll see how to force WordPress to display Foundation Top Bar as a navigation menu.

WordPress should print something like the following HTML:

<nav class="top-bar" data-topbar role="navigation">
   <ul class="title-area">
   <li class="name">
      <h1><a href="#">My Site</a></h1>
   </li>
   <!-- Remove the class "menu-icon" to get rid of menu icon. Take out "Menu" to just have icon alone -->
   <li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
   </ul>

   <div class="top-bar-section">
      <!-- Left Nav Section -->
      <ul class="left">
         <li class="active"><a href="#">Right Button Active</a></li>
         <li class="has-dropdown">
            <a href="#">Left Button Dropdown</a>
            <ul class="dropdown">
               <li><a href="#">First link in dropdown</a></li>
               <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
         </li>
      </ul>
   </div>
</nav>

To achieve our goal, add the following code to your header.php file or to whichever template file contains the navigation menu:

<nav class="top-bar" data-topbar role="navigation">
   <ul class="title-area">
      <li class="name">
         <h1><a href="#"><?php echo get_bloginfo(); ?></a></h1>
      </li>
      <li class="toggle-topbar menu-icon"><a href="#"><span><!--<?php echo get_bloginfo(); ?>--></span></a></li>
   </ul>
   <?php wp_nav_menu( array(
      'theme_location'     => 'primary',
      'container_class'    => 'top-bar-section',
      'menu_class'         => 'left',
      'walker'             => new Custom_Foundation_Nav_Menu()) ); ?>
</nav>

We have set the Foundation CSS class top-bar-section, along with a custom Walker class, on the navigation menu’s container, location and alignment. Now, we can define Custom_Foundation_Nav_Menu:

class Custom_Foundation_Nav_Menu extends Walker_Nav_Menu {
   public function start_lvl( &$output, $depth = 0, $args = array() ) {
      $indent = str_repeat("\t", $depth);

      // add the dropdown CSS class
      $output .= "\n$indent<ul class=\"sub-menu dropdown\">\n";
   }
   public function display_element( $element, &$children_elements, $max_depth, $depth = 0, $args, &$output ) {

      // add 'not-click' class to the list item
      $element->classes[] = 'not-click';

      // if element is current or is an ancestor of the current element, add 'active' class to the list item
      $element->classes[] = ( $element->current || $element->current_item_ancestor ) ? 'active' : ’;

      // if it is a root element and the menu is not flat, add 'has-dropdown' class 
      // from https://core.trac.wordpress.org/browser/trunk/src/wp-includes/class-wp-walker.php#L140
      $element->has_children = ! empty( $children_elements[ $element->ID ] );
      $element->classes[] = ( $element->has_children && 1 !== $max_depth ) ? 'has-dropdown' : ’;

      // call parent method
      parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
   }
}

As you can see, we’ve redefined the start_lvl and display_element methods. The first one generates the markup of the opening ul tag and assigns the dropdown CSS class.

The second method, display_element, is described in the Trac: It’s a method to “traverse elements to create a list from elements,” so it is a good place to make changes to the menu items.

Here we’ve accessed the has_children, classes, current and current_item_ancestor properties and passed the updated $element object to the parent display_element method. That’s enough to achieve our goal, but we could do much more on the menu items. If you call var_dump on the $element object, you’ll see all of the available properties at your disposal to build more advanced navigation menus.

And, as we’ll see in a moment, we can add new properties to the object.

The Foundation Top Bar on a WordPress website
The Foundation Top Bar on a WordPress website.

The menu is up and running, but we may want deeper customization. In the next example, we’ll get more from our navigation menu, allowing the website administrator to prepend icons to the items’s titles the easy way: directly from the administration panel. In our example, we’ll use Foundation Icon Fonts 3.

Adding Fields To The WordPress Menu Items’ Editing Box

Before we start coding, let’s open the menu editing page and make sure that all of the advanced menu properties in the “Screen Options” tab are checked.

Screen Options Tab
WordPress’ “Screen Options” tab on the administration page for menus.

Each checkbox enables or disables certain fields in the menu item’s editing box:

Menu item edit box
The menu items’ editing box.

But we can do more than add or change field values. The menu items are considered specific post types, and the values of the menu items’ fields are stored in the database as hidden custom fields. So we can add a new menu item’s field exactly as we do with regular posts’ custom fields.

Any kind of form field is allowed: inputs, checkboxes, textboxes and so on.

In the following example, I will show you how to add a simple text field that enables the website administrator to add a new property to the $item object. It will be stored as a custom field and will be used to show data in the website’s front end.

To do that, we will:

  1. register a custom field for the navigation menu item,
  2. save the new custom field’s value,
  3. set up a new Walker class for the edit menu tree.

Step 1: Register A Custom Field For The Nav Menu Item

First, we’ll have to register a new custom field for the navigation menu item in the functions.php file:

/**
 * Add a property to a menu item
 *
 * @param object $item The menu item object.
 */
function custom_nav_menu_item( $item ) {
   $item->icon = get_post_meta( $item->ID, '_menu_item_icon', true );
   return $item;
}
add_filter( 'wp_setup_nav_menu_item', 'custom_nav_menu_item' );

wp_setup_nav_menu_item filters the navigation menu’s $item object, allowing us to add the icon property.

Step 2: Save The User’s Input

When the user submits the form from the menu’s administration page, the following callback will store the fields’ values in the database:

/**
 * Save menu item custom fields' values
 * 
 * @link https://codex.wordpress.org/Function_Reference/sanitize_html_class
 */
function custom_update_nav_menu_item( $menu_id, $menu_item_db_id, $menu_item_args ){
   if ( is_array( $_POST['menu-item-icon'] ) ) {
      $menu_item_args['menu-item-icon'] = $_POST['menu-item-icon'][$menu_item_db_id];
      update_post_meta( $menu_item_db_id, '_menu_item_icon', sanitize_html_class( $menu_item_args['menu-item-icon'] ) );
   }
}
add_action( 'wp_update_nav_menu_item', 'custom_update_nav_menu_item', 10, 3 );

When updating the menu items, this action calls custom_update_nav_menu_item(), which will sanitize and update the value of the _menu_item_icon meta field.

Now we have to print the custom field’s markup.

Step 3: Set Up A New Walker For The Edit Menu Tree

The structure of the menu’s administration page is built by the Walker_Nav_Menu_Edit class, which is an extension of Walker_Nav_Menu. To customize the menu items’ editing boxes, we’ll need a new custom Walker_Nav_Menu child class based on the Walker_Nav_Menu_Edit class.

To set a custom Walker, this time we’ll need the following filter:

add_filter( 'wp_edit_nav_menu_walker', function( $class ){ return 'Custom_Walker_Nav_Menu_Edit'; } );

When fired, this filter executes an anonymous function that sets a custom class that will build the list of menu items.

Finally, the new Walker can be declared. We won’t reproduce the full code here. Just copy and paste the full Walker_Nav_Menu_Edit code from the Trac into your custom class and add the custom field markup as shown below:

class Custom_Walker_Nav_Menu_Edit extends Walker_Nav_Menu {
   public function start_lvl( &$output, $depth = 0, $args = array() ) {}
   public function end_lvl( &$output, $depth = 0, $args = array() ) {}
   public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
   ...

      <p class="field-xfn description description-thin">
         <label for="edit-menu-item-xfn-<?php echo $item_id; ?>">
            <?php _e( 'Link Relationship (XFN)' ); ?><br />
            <input type="text" id="edit-menu-item-xfn-<?php echo $item_id; ?>" class="widefat code edit-menu-item-xfn" name="menu-item-xfn[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $item->xfn ); ?>" />
         </label>
      </p>
      <p class="field-custom description description-thin">
         <label for="edit-menu-item-icon-<?php echo $item_id; ?>">
            <?php _e( 'Foundation Icon' ); ?><br />
            <input type="text" id="edit-menu-item-icon-<?php echo $item_id; ?>" class="widefat code edit-menu-item-icon" name="menu-item-icon[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $item->icon ); ?>" />
         </label>
      </p>
   ...
   }
}

Now, with the new input field in place, the website administrator will be able to add the new icon property to the menu $item object.

Custom menu item edit box
A customized version of the menu items’ editing box.

At this time, the Walker_Nav_Menu class won’t be able to access the new property value; so, the value of $item->icon would not be available to build the menu structure. To make it accessible, in the Custom_Foundation_Nav_Menu class of our previous example, we will redefine the start_el method. The easiest way to proceed is to copy the code from the Walker_Nav_Menu class and paste it in our custom class, editing it where necessary.

So, paste the code and jump to the bottom of the new start_el method and make the following edits:

public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {

   ...

   $item_output = $args->before;
   $item_output .= '<a'. $attributes .'>';

   if( !empty( $item->icon ) )
      $item_output .= '<i class="fi-' . $item->icon . '" style="margin-right: .4em"></i>';

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

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

No other changes are being done here except the condition that checks the value of the $item->icon property. If a value has been set, a new i element is attached to $item_output and assigned to the proper CSS class.

Finally, the following markup shows the new HTML menu structure.

<div class="top-bar-section">
   <ul id="menu-nav-menu" class="left">
      <li id="menu-item-46" class="menu-item menu-item-type-custom menu-item-object-custom current-menu-item current_page_item menu-item-home not-click active menu-item-46">
         <a href="https://localhost:8888/wordpress/">
            <i class="fi-social-smashing-mag"></i>
            <span>Home</span>
         </a>
      </li>
   </ul>
</div>

The image below shows the final result on the desktop.

Foundation 5 Topbar in WordPress
The custom Foundation 5 Top Bar integrated in a WordPress theme, enriched with icon fonts from Foundation Icon Font 3.

Final Notes

In this article, we’ve explored some of the most common uses of the Walker class. Note, however, that our examples do not cover all possible applications and alternative ways to take advantage of the class. But you’ll discover more just by making use of your imagination and your skills as a programmer.

And never lose sight of the official documentation:

Further Reading

Smashing Editorial (ml, dp, jb, al, jb, mrn)