Making breadcrumb with wp_nav_menu

I was looking for a quick and dirty method to output a breadcrumb navigation on a WP site, without requiring the installation of a plugin and also leveraging the built-in WP menu.

Here’s what I came up with. I’m curious to know if there is a better solution I am missing, and/or if there is anything obviously wrong with doing it this way. So far in my testing it works quite well.

class BreadcrumbWalker extends Walker_Nav_Menu {
    private $i_current_page_id;
    private $i_depth;
    private $a_output;

    function __construct() {
        // sets our current page so we know when to exit
        $this->i_current_page_id = get_queried_object()->ID;
    }

    function start_lvl(&$output, $depth=0, $args=array()) {
        // increment the depth every time a new ul is entered
        $this->i_depth++;
    }

    function end_lvl(&$output, $depth=0, $args=array()) {
        // decrement the depth when we exit a ul
        $this->i_depth--;
    }

    function start_el(&$output, $item, $depth=0, $args=array()) {
        // if this value is zero, we're starting a new branch
        if($item->menu_item_parent == 0) {
            // reset the output array and depth counters
            $this->a_output = array();
            $this->i_depth = 0;
        }
        // if we haven't set the representative menu item for this depth, do so
        if(!isset($this->a_output[$this->i_depth])) {
            $this->a_output[$this->i_depth] = '<a href="' . get_permalink($item->object_id) . '">' . $item->title . '</a>';
        }
    }

    function end_el(&$output, $item, $depth=0, $args=array()) {
        if($this->i_current_page_id == $item->object_id) {
            // check to see if this is our last item, if so display the breadcrumb
            if($this->i_depth > 0) {
                // but only show it if we actually have a breadcrumb trail
                $this->display_breadcrumb();
            }
        } else {
            // if not, unset the item for this depth since this isn't what we're going to display
            unset($this->a_output[$this->i_depth]);
        }
    }

    function display_breadcrumb() {
        // implode our array into a string
        echo implode(' &raquo; ', $this->a_output);
    }
}

Basically what I’m doing is using the Walker_Nav_Menu methods to set an output array that uses the depth as its key, and once the end_el method is called, if it’s not the ID of the page that’s the end of the breadcrumb, it unsets the item at that depth and keeps iterating over the array. If it is the correct ID, it calls the display_breadcrumb method to output the menu.

Thanks in advance for any input. I feel like there’s probably a better way to do this, but at the moment am stumped as to what that might be.

Cheers.

3 s
3

I couldn’t believe there is not a single FREE plugin available that does this. So I wrote my own function. Here you go. Just copy this to your functions.php:

function my_breadcrumb($theme_location = 'main', $separator=" &gt; ") {
    
    $theme_locations = get_nav_menu_locations();
    
    if( ! isset( $theme_locations[ $theme_location ] ) ) {
        return '';
    }
    
    $items = wp_get_nav_menu_items( $theme_locations[ $theme_location ] );
    _wp_menu_item_classes_by_context( $items ); // Set up the class variables, including current-classes
    $crumbs = array();

    foreach($items as $item) {
        if ($item->current_item_ancestor || $item->current) {
            $crumbs[] = "<a href=\"{$item->url}\" title=\"{$item->title}\">{$item->title}</a>";
        }
    }
    echo implode($separator, $crumbs);
}

Then, anywhere in your theme you can output the breadcrumb like this:

<?php my_breadcrumb('menu-slug'); ?>

This code can easily be extended, by omitting the $theme_location parameter and getting a list of every available menu, only outputting the result if the $crumbs array isn’t empty. I may write a blog post about this or turn it into a free plugin soon. Keep you updated.

You could also add a class or differnt markup the current item by checking for $item->current.

Leave a Comment