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).
Further Reading on SmashingMag:
- Rapid Front-End Prototyping With WordPress
- How To Create And Customize A WordPress Child Theme
- Create A Responsive, Mobile-First WordPress Theme
- Creating A Complete Web App In Foundation For Apps
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).
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="http://example.com/wordpress/category/coding/">Coding</a>
<ul class="children">
<li class="cat-item cat-item-39">
<a href="http://example.com/wordpress/category/coding/php/">PHP</a>
</li>
</ul>
</li>
<li class="cat-item cat-item-11">
<a href="http://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 name | Defined in | Extends |
---|---|---|
Walker_Page |
post-template.php | Walker |
Walker_PageDropdown |
post-template.php | Walker |
Walker_Category |
category-template.php | Walker |
Walker_CategoryDropdown |
category-template.php | Walker |
Walker_Category_Checklist |
template.php | Walker |
Walker_Comment |
comment-template.php | Walker |
Walker_Nav_Menu |
nav-menu-template.php | Walker |
Walker_Nav_Menu_Checklist |
nav-menu.php | Walker_Nav_Menu |
Walker_Nav_Menu_Edit |
nav-menu.php | Walker_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:
Argument | Type | Description |
---|---|---|
$output |
string | passed by reference; used to append additional content |
$depth |
int | depth of the item |
$args |
array | an 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 ) {}
Argument | Type | Description |
---|---|---|
$output |
string | passed by reference; used to append additional content |
$object |
object | the data object |
$depth |
int | depth of the item |
$args |
array | an array of additional arguments |
$id |
int | current 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:
Argument | Type | Description |
---|---|---|
$element |
object | the data object |
$children_elements |
array | list of elements to continue traversing |
$max_depth |
int | maximum depth to traverse |
$depth |
int | depth of current element |
$args |
array | an array of arguments |
$output |
string | passed 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.
Argument | Type | Description |
---|---|---|
$elements |
array | an array of elements |
$max_depth |
int | maximum 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.
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.
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 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.
Each checkbox enables or disables certain fields in the menu item’s 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:
- register a custom field for the navigation menu item,
- save the new custom field’s value,
- 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.
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="http://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.
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: