Customizing Breadcrumbs

Within the Theme Blvd framework, breadcrumbs are quite flexible. When we integrate compatibility for plugins that create their own post types or pages of content — like bbPress, WooCommerce and even our own Portfolios plugin — they generally involve filtering onto the breadcrumbs trail, in some way.

Over the years, I’ve found it’s important to be able to filter onto the breadcrumbs in a way that doesn’t effect the HTML markup. When modifying the breadcrumbs, we’re almost always wanting to modify the data of the trail, and not the actual visual appearance, or structure of the HTML.

So, while we allow you to filter the final HTML output of the breadcrumbs, as most themes only do, we also add earlier steps in the process. We’ve broken up the breadcrumbs into (1) whether they display, (2) the construction of the trail’s data, and (3) the construction of the actual HTML output. These steps are in three separate, filterable functions.

Having this separation adds an extra layer of protection against future theme updates that may alter the HTML, as well as how the breadcrumbs design may be altered slightly from theme-to-theme. In other words, if we add WooCommerce compatibility to the breadcrumbs trail, we don’t want this interfere with adding a special CSS class to each item in the trail, throughout your website.

Whether Breadcrumbs Display

Before we dive into customizing the actual breadcrumbs trail, let’s first take a step back and look at what determines if breadcrumbs display or not.

From your theme options page, you can choose to show or hide breadcrumbs globally throughout your site. And then, when editing an individual page or standard post, you can choose to show or hide breadcrumbs there, as well.

Well, that’ll work in most cases, but for more custom circumstances, this is a bit limiting. There’s a filter you can use, themeblvd_show_breadcrumbs, to show or hide breadcrumbs, at any time. Simply use the filter to return true or false.

Examples

Display Breadcrumbs on Custom Post Types

Let’s say you’ve disabled breadcrumbs globally on your theme options page, but you want them to display only on the custom post types, foo and bar.

function my_show_breadcrumbs( $show ) {

	if ( is_singular( array('foo', 'bar') ) ) {
		$show = true;
	}

	return $show;
}
add_filter('themeblvd_show_breadcrumbs', 'my_show_breadcrumbs');
Display Breadcrumbs Only on Standard Posts and Pages

A fairly common issue people face is compatibility with other plugins introducing custom post types. There is a simple, default output for custom post types by the theme framework, but maybe you just want to disable breadcrumbs across all of these third-party added post types.

In the following example, it’s assumed you’ve got breadcrumbs enabled globally from your theme options. And then in our code, we want to disable the breadcrumbs any time we’re viewing a single custom post type (i.e. it’s not a page, attachment, or single post).

function my_show_breadcrumbs( $show ) {

	if ( is_single() && get_post_type() != 'post' ) {
		$show = false;
	}

	return $show;
}
add_filter('themeblvd_show_breadcrumbs', 'my_show_breadcrumbs');

Modifying the Breadcrumbs Trail Data

If you’re making customizations to breadcrumbs, this is probably the most common thing that you’ll want to filter. Before the final HTML ever gets put together, the theme framework builds the data for the breadcrumbs trail as a set of arrays.

There are actually two filters you can use to modify this data:

  1. themeblvd_pre_breadcrumb_parts — Before “Home” link has been added.
  2. themeblvd_breadcrumb_parts — After “Home” link has been added.

Here’s an example of how the data is structured and returned for the breadcrumbs trail, for a single post:

Array
(
    [0] => Array
        (
            [link] => http://yoursite.com
            [text] => Home
            [type] => home
        )

    [1] => Array
        (
            [link] => http://yoursite.com/category/portfolio
            [text] => Portfolio
            [type] => category
        )

    [2] => Array
        (
            [link] => 
            [text] => A Post With Everything In It
            [type] => single
        )

)

In the above data structure, you’ll need at least the link and text in each array of your data structure. The type isn’t really used for anything all that relevant, other than adding a CSS class to the final HTML output. For consistency, I like to usually add something, that makes sense for the scenario. Also notice that the third array has an empty link string — this means that the output won’t make that into a link. In a typical breadcrumb trail, the last item (i.e. the current page) is generally not a link.

Note: The above example is printing the data from themeblvd_breadcrumb_parts, but you can keep things more simple by using themeblvd_pre_breadcrumb_parts, because in most cases, you’ll be leaving the “Home” link alone at the start of the trail.

Examples

Adding A “Blog” Page Link to the Trail of All Single Posts

If you’re like most of us, the homepage of your website isn’t a blog. In this case, you’ve probably created a another page on your website that serves as your blog. So, you may want to reflect this in the breadcrumbs trail for all of your single blog posts.

function my_breadcrumb_parts( $parts ) {

    if ( is_singular( array('post') ) ) { // standard posts only
        $parts = array_merge(array(
            array(
                'link'  => 'http://yoursite.com/blog',
                'text'  => 'Blog',
                'type'  => 'page',
            )
        ), $parts);
    }

    return $parts;

}
add_filter('themeblvd_pre_breadcrumb_parts', 'my_breadcrumb_parts');
Adding A “Blog” Page Link to the Trail of All Single Posts and Archives

We can also expand the last example, and make things a bit more complex. Let’s say instead of just the single post, we want to add our “Blog” page link to post archives, as well.

function my_breadcrumb_parts( $parts ) {

    if ( is_singular( array('post') ) || ( is_archive() && ! is_post_type_archive() ) ) {
        $parts = array_merge(array(
            array(
                'link'  => 'http://yoursite.com/blog',
                'text'  => 'Blog',
                'type'  => 'page',
            )
        ), $parts);
    }

    return $parts;

}
add_filter('themeblvd_pre_breadcrumb_parts', 'my_breadcrumb_parts');
Creating a New Trail for a Custom Post Type

Probably the most common customization the breadcrumbs trail would be to add support for a custom post type. This can actually be quite complex because we’ll have to construct a new trail for the single post and associated archives.

For an example, let’s use what’s actually in our Portfolios plugin. Here we’ve got a custom post type, portfolio_item, with two taxonomies, portfolio and portfolio_tag.

The portfolio taxonomy is hierarchical and so that makes it a bit more complex to work with, as we’ll want to account for parent terms of the current post’s term. And also, on our single posts, we’ll use this portfolio taxonomy for the trail.

function my_breadcrumb_parts( $parts ) {

    global $wp_query;

    // Single Portfolio Items
    if ( is_single() && 'portfolio_item' == get_post_type() ) {

        $parts = array(); // reset it

        // Portfolio taxonomy tree
        $portfolio = get_the_terms( get_the_id(), 'portfolio' );

        if ( $portfolio ) {
            $portfolio = reset( $portfolio );
            $parents = themeblvd_get_term_parents( $portfolio->term_id, 'portfolio' );
            $parts = array_merge( $parts, $parents );
        }

        // Single post title
        $parts[] = array(
            'link'  => '',
            'text'  => get_the_title(),
            'type'  => 'single'
        );

    }

    // Portfolios
    if ( is_tax( 'portfolio' ) ) {

        // Parent portfolios
        $portfolio_obj = $wp_query->get_queried_object();
        $current_portfolio = $portfolio_obj->term_id;
        $current_portfolio = get_term( $current_portfolio, 'portfolio' );

        if ( $current_portfolio->parent && ( $current_portfolio->parent != $current_portfolio->term_id ) ) {
            $parents = themeblvd_get_term_parents( $current_portfolio->parent, 'portfolio' );
            $parts = array_merge( $parts, $parents );
        }

        // Add current portfolio
        $parts[] = array(
            'link'  => '',
            'text'  => $current_portfolio->name,
            'type'  => 'category'
        );
    }
    
    // Portfolio Tags
    if ( is_tax( 'portfolio_tag' ) ) {
        $parts[] = array(
            'link'  => '',
            'text'  => single_term_title( '', false ),
            'type'  => 'tag'
        );
    }

    return $parts;
}
add_filter('themeblvd_pre_breadcrumb_parts', 'my_breadcrumb_parts');

Modifying the Breadcrumbs HTML Output

And finally, this is pretty much like any other filter. Before the breadcrumbs are outputted onto the screen, you’ve got a chance to filter the trail, using themeblvd_breadcrumbs_trail.

Examples

Adding a Class to the Wrapping <ul>

Here, we’ll add the class “small-list” to wrapping unordered list in the HTML output.

function my_breadcrumbs_trail( $html ) {
    return str_replace('class="breadcrumb"', 'class="breadcrumb small-list"', $html);
}
add_filter('themeblvd_breadcrumbs_trail', 'my_breadcrumbs_trail');
Adding a Class to Each <li>

And here, we’ll add the class “item” to each list item.

function my_breadcrumbs_trail( $html ) {
    return str_replace('<li>', '<li class="item">', $html);
}
add_filter('themeblvd_breadcrumbs_trail', 'my_breadcrumbs_trail');
Completely Re-Skin With Your Own HTML Markup

Or maybe you just want to create your own custom HTML structure for the entire thing. In that case, we’ll need to pass in the $parts parameter, which will contain the data to be looped through for the breadcrumb trail (i.e. what we were discussing filtering here).

function my_breadcrumbs_trail( $html, $atts, $parts ) {

    $html = '<div class="my-breadcrumb-trail">';

    foreach ( $parts as $part ) {

        // $part['text'] - The text of the item in the trail
        // $part['link'] - The link for the item in the trail

        $html .= '<span class="item">';

        if ( $part['link'] ) {
            $html .= sprintf('<a href="%s">%s</a>', $part['link'], $part['text']);
        } else {
            $html .= $part['text'];
        }

        $html .= '</span>';
    }

    $html .= '</div>';

    return $html;
}
add_filter('themeblvd_breadcrumbs_trail', 'my_breadcrumbs_trail', 10, 3);

The above code will give us an output something like this:

<div class="my-breadcrumb-trail">
    <span class="item"><a href="http://yoursite.com">Home</a></span>
    <span class="item"><a href="http://yoursite.com/portfolio/">Portfolio</a></span>
    <span class="item">A Post With Everything In It</span>
</div>