We recently needed to customize the breadcrumb on WooCommerce Product Category pages for a client’s eCommerce site. The client wanted the current product category in the breadcrumb to be replaced with a dropdown populated with the current category along with other categories of the same root ancestor (not all product categories). If your breadcrumb needs are different, you can still read on and get a general idea of what/where you need to customize.

1. Create the dropdown

Since our client wanted the related “family” of the current product category as options, we wrote three separate functions in my-theme/functions.php to accomplish this task. Note that we use permalinks for the dropdown option values – that way we can redirect to the appropriate page when the user selects a different product category.

//Gets the root parent of a term for a particular taxonomy
function da_get_root_term($taxonomy, $cat_id) {
	$term = get_term_by( "id", $cat_id, $taxonomy );
	if ( $term && $term->parent > 0 ) {
		return da_get_root_term( $taxonomy, $term->parent );
	} else return $term;
}
//Gets hierarchical dropdown (as html string) for specific taxonomy field
function da_get_terms_dropdown( $taxonomy, $parent_id,  $selected_id = 0,
									$includeBlankSelect = false,
									$elem_id = "", $elem_class = ""){

	$html = "";

	//get all the options
	$options = da_get_terms_indented( $taxonomy, $parent_id, $selected_id );

	//only return dropdown if options exist - otherwise return empty string
	if( !empty($options) ){
		$html .= '<select id="' .$elem_id. '" class="' .$elem_class. '">' ;
		$html .= ( ($includeBlankSelect) ? "<option></option>" : "" );
		$html .= $options. '</select>';
	}
	return $html;
}
//Recursive helper method, returns string of <option> in hierarchical order and formatted 
function da_get_terms_indented( $taxonomy, $parent_id, $selected_id = 0, $level = 0 ){	
	$options = "";

	//format with spaces to show hierarchy
	$prefix = str_repeat( " ", $level * 3 );
	if( !empty($prefix) ) $prefix .= " ";

	//get the direct children of specific parent
	$args = array(
		'orderby'    => 'title',
		'order'      => 'asc',
		'hide_empty' => false,
		'parent'    => $parent_id,
		'hierarchical' => false
	);
	$terms = get_terms( $taxonomy, $args );

	if ( count($terms) > 0 ){
		foreach ( $terms as $term ) {

			//mark the option as selected if it matches the selected_id param
			$is_selected = "";
			if( $selected_id > 0 && $selected_id == $term->term_id ) 
				$is_selected = 'selected="selected"';

			$options .= '<option value="' . get_term_link( $term ) . '" ' . $is_selected . '>' 
						. $prefix . $term->name . '</option>';

			//get the direct children options of this child
			$options .= da_get_terms_indented( $taxonomy, $term->term_id, $selected_id, $level+1 );
		}
	}
	return $options;
}

Depending on your number of categories, number of users, hosting environment, etc. you may need to cache your product category details. For an alternative and more efficient version of the code used in this step, check out our post Improve WordPress Performance with Efficient Code and Caching Custom Queries.

2. Add the dropdown to the breadcrumb

Make a copy of plugins/woocommerce/templates/global/breadcrumb.php and paste it in themes/your-theme/woocommerce/global/breadcrumb.php (modifying plugin files is strongly discouraged). Add an else if within the loop to specifically handle product category pages. (The if takes care of elements within the breadcrumb with a defined url that are not the final element in the list.) You also have to check if the custom breadcrumb has been applied already – this is because additional breadcrumb pieces without urls can be added after the product category, i.e. page number. Using the global $wp_query, you can get the current queried object (a product category) and pass along to da_get_root_term and da_get_terms_dropdown. After that, display the dropdown if it was generated, otherwise show the breadcrumb text (i.e. the product category has no ancestors or descendants). The final version of our custom breadcrumb.php looked like this:

if ( ! empty( $breadcrumb ) ) {

	echo $wrap_before;

	$custom_breadcrumb = false;
	foreach ( $breadcrumb as $key => $crumb ) {

		echo $before;

		if ( ! empty( $crumb[1] ) && sizeof( $breadcrumb ) !== $key + 1 ) {
			echo '<a href="' . esc_url( $crumb[1] ) . '">' . esc_html( $crumb[0] ) . '</a>';
		} else if(is_product_category() && !$custom_breadcrumb){
			global $wp_query;
			$bc_cat = $wp_query->get_queried_object();

			//get ancestor category, then get dropdown of all children
			$main_cat = da_get_root_term("product_cat", $bc_cat->term_id);
			$bc_dropdown = da_get_terms_dropdown(
					"product_cat", $main_cat->term_id, $bc_cat->term_id
				);

			if(!empty($bc_dropdown))
				echo $bc_dropdown;
			else
				echo $crumb[0];

			$custom_breadcrumb= true;
		} else {
			echo esc_html($crumb[0]);
		}	

		echo $after;

		if ( sizeof( $breadcrumb ) !== $key + 1 ) {
			echo $delimiter;
		}

	}

	echo $wrap_after;

}

3. Redirect to the selected category page

Add the code below to a new javascript file (your-theme/js/jquery.woocommerce_breadcrumb.js) and make sure to load it on the appropriate pages in your-theme/functions.php.

jQuery(function($){
	//redirect on breadcrumb dropdown change
	$(".woocommerce-breadcrumb select").change(function(){
		$url = $(this).find(":selected").val();
		window.location = $url;
	});
});