Advertisement
Creative Coding

Setting Active Navigation Elements When WordPress Doesn't

by

WordPress does an excellent job highlighting current standard posts, pages or taxonomies when you include them in a navigation menu. But when you create a custom post or custom taxonomy everything goes wrong and the navigation stops highlighting the current page. Fortunately there is a workaround, you can manually specify which menu element highlights when you are showing custom content.


How This Will Work

The solution is simple. We've written a few lines of code that create a settings page were you will specify the menu elements to highlight for every custom content type. The next step is to override the default WordPress navigation Walker class to generate a highlight class when needed. Simple and effective.


Step 1 Creating and Using a Custom Include File

Create a new file called navigation.php and include it from the functions.php file.

include_once ( get_template_directory() . '/navigation.php' );

Now we're ready to start with the real code.


Step 2 Creating the Settings Page

First, register a new settings group to generate a new wp-admin settings page. In your empty navigation.php file insert the following code.

add_action( 'admin_init', 'ns_register_navigation_settings' );
function ns_register_navigation_settings() {
	register_setting( 'ns_navigation', 'ns_navigation_predefined_values' );
}

Then generate a new menu element to access our new settings page in wp-admin.

add_action('admin_menu', 'ns_navigation_options');
function ns_navigation_options() {
	add_submenu_page( 'themes.php', 'Predefined Menus', 'Predefined Menus', 'edit_theme_options', 'menu-defaults', 'menu_defaults_page' );
}

The menu_defaults_page() function prints the settings page inside WordPress Admin. Before printing the form inputs get_option('ms_navigation_predefined_values') requests the values stored in the database and stores them in $ns_navigation_predefined_values as an array.

In this case there's nothing stored yet so the values are empty. Using settings_field() is required for printing related and required hidden fields and for security handling too. The rest of the code prints the input elements using the values in $ns_navigation_predefined_values.

The settings page is now available but empty. We need to populate it with all the available custom posts and taxonomies that have been generated and the available menu elements to match those values. Insert the following code.

function menu_defaults_page() {
    ?>
    <div class="wrap">
    	<div class="icon32" id="icon-options-general"><br></div>
    	<h2><?php _e('Predefined menus for custom posts and taxonomies'); ?></h2>
		<form method="post" action="options.php">
	
	<?php
	$ns_navigation_predefined_values = get_option('ns_navigation_predefined_values');
	settings_fields( 'ns_navigation' );
	?>
    
    <h3 class="title"><?php _e('Pages'); ?></h3>
    <table class="form-table" cellpadding="0" cellspacing="0">
    
	<?php	
	foreach (ns_get_post_types() as $k => $v) {
	?>
        <tr valign="top">
        <th scope="row"><?php echo $v ?></th>
        <td>
            <?php 
			$current_dropdown_value = get_option('ns_navigation_predefined_values');
			wp_dropdown_pages( array( 'name' => 'ns_navigation_predefined_values[' . $k . ']', 'echo' => 1, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => $current_dropdown_value[$k] ) );
			?>
        </td>
        </tr>
	<?php
    }
	?>
	
    </table>
    <?php if (ns_get_taxonomies()): ?>
    <br /><hr />
    <h3 class="title"><?php _e('Categories') ?></h3>
    <table class="form-table" cellpadding="0" cellspacing="0">
    
	<?php	
	foreach (ns_get_taxonomies() as $k => $v) {
	?>
        <tr valign="top">
        <th scope="row"><?php echo $v ?></th>
        <td>
            <?php 
			$current_dropdown_value = get_option('ns_navigation_predefined_values');
			wp_dropdown_pages( array( 'name' => 'ns_navigation_predefined_values[' . $k . ']', 'echo' => 1, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => $current_dropdown_value[$k] ) );
			?>
        </td>
        </tr>
	<?php
    }
	?>
	
    </table>
    <?php endif; ?>
    <p class="submit">
    <input type="submit" class="button-primary" value="<?php _e('Update'); ?>" />
    </p>
    </form>
    </div>
    
	<?php
}

The settings page is now created but we still need to define the functions called in the code above. Insert the following code.

function ns_get_post_types() {
	$post_types = get_post_types(array('public' => true, '_builtin' => false), 'objects');
	foreach ( $post_types as $k => $v ) {
		$ns_registered_post_types->$k = $v->labels->name;
	}
	return $ns_registered_post_types;
}

function ns_get_taxonomies() {
	$taxonomies_types = get_taxonomies(array('public' => true, '_builtin' => false), 'objects');
	foreach ( $taxonomies_types as $k => $v ) {
		$ns_registered_taxonomies_types->$k = $v->labels->name;
	}
	return $ns_registered_taxonomies_types;
}

The function ns_get_post_types retrieves all the available post types and outputs only those that are custom. The function ns_get_taxonomies does the same, but for taxonomies of course.


Step 3 Making It Work in the WordPress Theme

We have the settings page declared and a few custom posts and taxonomies declared. The next step is to make it work in the theme we're using. For testing purposes we're working with WordPress' Twenty Eleven theme but this code should work with any theme.

Let's modify the WordPress Menu Walker class to override the default output. We're reading our settings and using the values to add a new current_page_item ns_current_page_item class in the respective page when we're displaying the custom post loop or related single page.

class NS_Walker_Nav_Menu extends Walker_Nav_Menu {
	function start_el(&$output, $item, $depth, $args) {
		global $wp_query;
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
		
		if ( isset( $args->current_nav_element ) ) {
			$current_nav_element = $args->current_nav_element;
		}
			
		$class_names = $value = '';

		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
		$classes[] = 'menu-item-' . $item->ID;
		$classes[] = 'page-gui-' . $item->object_id;

		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
		$class_names = ' class="' . esc_attr( $class_names );
		
		if ($current_nav_element == $item->object_id) {
			$class_names.= ' current_page_item ns_current_page_item';
		}
		
		$class_names.= '"';
		
		$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
		$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';

		$output .= $indent . '<li' . $id . $value . $class_names .'>';

		$attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
		$attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
		$attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
		$attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

		$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 new NS_Walker_Nav_Menu Class reads the navigation values stored in an array before printing. In this case using an if() control structure to evaluate if the current navigation element matches with the previous stored value for the page WordPress is currently printing. If the condition is true then the classes "current_page_item" and "ns_current_page_item" are added to the existing classes stored in the $class_names variable.

Then we need to use one more custom function. When we call it, this function will print the menu in the theme.

function ns_wp_nav_menu($args) {
	global $post;
	$ns_walker = new NS_Walker_Nav_Menu();
	$args['walker'] = $ns_walker;
	
	$ns_navigation_predefined_values = get_option('ns_navigation_predefined_values');

	$custom_post_type = get_post_type($post);
	$available_post_types = (array) ns_get_post_types();
	$taxonomy_type = get_queried_object();
	$taxonomy_type = $taxonomy_type->taxonomy;
	$available_taxonomy_types =  (array) ns_get_taxonomies();
	
	if (is_singular($custom_post_type) && array_key_exists($custom_post_type, $available_post_types)) {
		$args['current_nav_element'] = (function_exists('icl_object_id')) ? icl_object_id($ns_navigation_predefined_values[$custom_post_type], 'page') : $ns_navigation_predefined_values[$custom_post_type];
	} elseif (is_tax($taxonomy_type) && array_key_exists($taxonomy_type, $available_taxonomy_types)) {
		$args['current_nav_element'] = (function_exists('icl_object_id')) ? icl_object_id($ns_navigation_predefined_values[$taxonomy_type], 'page') : $ns_navigation_predefined_values[$taxonomy_type];
	} else {
		unset($args['current_nav_element']);
	}
	
	wp_nav_menu($args);
}

The ns_wp_nav_menu() is created to simplify the use of the built-in wp_nav_menu(). The first step is to force the function to load the new Walker class using $ns_walker = new NS_Walker_Nav_Menu() and adding to the parameters array using $args['walker'] = $ns_walker;.

Instead of always passing the required parameters to the function this is included for default. In this specific case the function reads the current post and even reads the translated page if the WPML plugin is enabled on your WordPress website.

First evaluate if the page is in single view using is_singular() and get from the database the corresponding stored value. The second possible choice to evaluate is if the current page is a taxonomy query using is_tax(). If not, then there is nothing to select and the code releases the current navigation element using unset($args['current_nav_element'])


Step 4 Printing the Navigation Menu in a WordPress Theme

Open the header.php file in your Twenty Eleven theme, find the wp_nav_menu() function, approximately on Line 118, and replace with ns_wp_nav_menu keeping the same parameters and nothing else because the new function handles the rest of the required parameters by default. The new code should look like this:

	<?php ns_wp_nav_menu( array( 'container_class' => '', 'theme_location' => 'primary' ); ?>

This function uses the same arguments as the standard wp_nav_menu function so feel free to tweak as much as you want or need to.

Open style.css too and replace the code on line 617 with:

	#access .current-menu-item > a, #access .current-menu-	ancestor > a, #access .current_page_item > a, #access .current_page_ancestor > a, #access .ns_current_page_item > a {
    font-weight: bold;
}

Step 5 Get the Most From Your Enhanced Navigation System

You have custom posts, custom taxonomies and you have created new pages with templates to show these custom loops. You probably have created a new menu in your wp-admin and added those pages too. Open the predefined menus settings page located under Appearance and set the selected pages for every custom post and taxonomy you have created.

When you display the custom post or the single page related to this custom post the navigation will highlight the matched menu element.


Conclusion

There are many ways to achieve this same result but after a few published projects using this approach I found this is the best and most user-friendly.

Anyway this is only the beginning of all the possibilities you can achieve when you understand this code and start making modifications for your personal needs.

I encourage you to keep researching about the navigation Walker class. There are a lot of possibilities hidden in there, you can bet on it.

Related Posts
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Creating Custom Queries for AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the second in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options.Read More…
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Assigning Categories and Taxonomy Terms to AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the first in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options. Read More…
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML: Creating an Archive TemplateCreating wordpress theme from html 400
    If you've been working your way through this series, you now have a functioning theme with two page templates. The steps I've demonstrated to this point are: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file creating template files for static pages. Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML - Creating a Page TemplateCreating wordpress theme from html 400
    So far in this series, I've shown you how to create a fully functioning WordPress theme from static HTML. We've covered the following steps: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file. At the moment, your theme only has one template file for displaying content—the index.php file. A powerful feature of WordPress is the ability to use template files for different kinds of content.Read More…
  • Code
    Plugins
    A Better Forum List Widget for bbPressBbpress
    When bbPress was still a standalone installation, I had tried it out and wasn't really impressed. Things were clunky and it didn't always work the way it was supposed to. After languishing for a few years, Automattic decided to take bbpress and turn it into a plugin, improving the functionality leaps and bounds and making it a strong contender amongst other forum option for WordPress.Read More…