If you are a WordPress user or developer with some programming knowledge, you may have come across the Walker class at some point, and if not, it’s a pretty handy one to know. In a nutshell it allows you to traverse “tree-like” HTML data structures, like WordPress menus that consist of unordered lists.
You may want to use this class if you have a menu of pages, and you only want to output particular sub-pages of a certain page X — say the second level subpages of page X. You could also use it if you want to add classes to elements based on what level of the hierarchy they occur or whether or not they have children.
In order to do something with your hierarchical structure, you need to create your own custom Walker class. In the case of menus, you can simply extend Walker_Nav_Menu which handles most of the menu logic for us, and really only requires us to define the functions we need from the following:
- start_lvl : Add code to manipulate the opening tag of a list, ie the <ul>
- end_lvl: Add code to manipulate the closing tag of a list, ie the </ul>
- start_el: Add code to manipulate the opening tag of a list item, ie <li>
- end_el: Add code to manipulate the closing tag of a list item, ie </li>
Pretty straightforward right?
Basically, each function builds up the HTML output of our menu by appending to a string variable, $output. So end_lvl would append ‘</ul>’ to the end of our partially built list in $output, which consists of everything that’s supposed to come before that particular </ul>.
$output is one of the arguments that is passed to these 4 functions. Another useful one they all receive is $depth, which helps us target specific levels of the hierarchy. Depth refers to the level of the <li> elements. So if you have the following structure:
<ul>
<li>Home Page <!-- Depth = 0 -->
<ul> <!-- Depth = 0 -->
<li>Sub-Page</li> <!-- Depth = 1 -->
</ul>
</li>
<li>Contact Page</li> <!-- Depth = 0 -->
</ul>
The <ul> tag is considered to be on the same depth as its parent <li> (note that the overarching <ul> has no depth). We descend a level with each subsequent nested <li> tag.
The element functions also include the $item argument, which is the element object itself. For menus this includes useful information like the URL, title, page ID, etc of our page.
One place I found I needed the Walker class was for integrating a WordPress menu with Bootstrap’s navigation structure. What I needed was to add a special class to <li> elements only if they contained children elements. A particular class was also needed for opening <ul> tags as well. Below is the code for this:
class Bootstrap_Nav_Walker extends Walker_Nav_Menu {
function start_lvl(&$output, $depth=0, $args=array()) {
$output .= '<ul role="menu" class="dropdown-menu">';
}
function end_lvl(&$output, $depth=0, $args=array()) {
$output .= '</ul>';
}
function start_el(&$output,
$item,
$depth=0,
$args=array())
{
if( $this->has_children ) {
$output .= '<li class="dropdown">
<a data-toggle="dropdown"
class="dropdown-toggle"
href="#">';
$output .= $item->title.
' <b class="caret"></b></a>';
} else {
$output .= '<li><a href="'.
$item->url . '">'.
$item->title . '</a>';
}
}
function end_el(&$output, $item, $depth=0, $args=array()) {
$output .= '</li>';
}
}
All functions are shown for illustrative purposes, but we technically didn’t need to include end_lvl or end_el because we are not changing the default behavior. To determine if an <li> element has children, we use the very useful class variable has_children which updates for each start_el to determine whether the element has children or not. Simple as that!
In order to use your Walker class, add it to your functions.php file and then call wp_nav_menu with a new instance of your Walker class as an argument:
wp_nav_menu( array( 'theme_location' => 'header-menu',
'walker' => new Bootstrap_Nav_Walker() ) );