I want to make minor css-changes based on the choice of the top (root) menu.
What’s the proper way to handle this in WP?
//edit//
It’s a website (pages), no blog, and basically, the colors of some links and some images should depend of the choosen top-menu-item.
Example, page-structure:
* Start * Products * Prod.categ. * A product * Services * List of services * Nested * Contact * Main office * List of staff * Abroad * Another list
Top level is in a topmenu, one-level, and level2&3 in nested left-menu.
Now minor coloured items in sidemenu changes depending on selected top-menu item.
//end edit//
regards,
/t
2 Answers
My answer adds a class to the <body>
element via the body_class
filter. This is probably the easiest way to apply extra formatting to any element on the page. The added classes are wpse14430_products
, wpse14430_services
or wpse14430_contact
(based on the slugs of the top pages in your example).
Using wp_nav_menu()
If you use wp_nav_menu()
to display the menu, WordPress builds a tree of the menu items. We can use this information to get the top page of the current item. Only problem: we need it in the <body>
tag, thus before the menu is rendered. The solution is to save the menu in an variable which we later echo ourselves.
Taking the Twenty Ten theme as an example, I move the wp_nav_menu()
call up to the first <?php
block:
$wpse14430_menu = wp_nav_menu( array(
'container_class' => 'menu-header',
'theme_location' => 'primary',
'echo' => false,
) );
And where we used to call it, we now echo our saved output:
echo $wpse14430_menu;
wp_nav_menu()
has an interesting filter, called after the menu items are ordered and have their classes. The classes already contain ancestor information. We hook into this filter and find the first item that is the current item or an ancestor of it:
add_filter( 'wp_nav_menu_objects', 'wpse14430_wp_nav_menu_objects' );
function wpse14430_wp_nav_menu_objects( $sorted_menu_items )
{
// The items are in menu order, so the first match is the top item
foreach ( $sorted_menu_items as $menu_item ) {
if ( $menu_item->current || $menu_item->current_item_ancestor ) {
$GLOBALS['wpse14430_top_page'] = get_post( $menu_item->object_id );
break;
}
}
return $sorted_menu_items;
}
Now we got the top page, and only need to add this to the body class:
add_filter( 'body_class', 'wpse14430_body_class_menu' );
function wpse14430_body_class_menu( $body_class )
{
if ( isset( $GLOBALS['wpse14430_top_page'] ) ) {
$body_class[] = 'wpse14430_' . $GLOBALS['wpse14430_top_page']->post_name;
}
return $body_class;
}
Using pages as the menu structure
If you don’t use wp_nav_menu()
but instead use the direct ordering of the pages, you can also check for ancestors. Remember that if don’t define a menu using the new menu system but still display the page list via wp_nav_menu()
(the fallback functionality), the above system will work for you.
add_filter( 'body_class', 'wpse14430_body_class_pages' );
function wpse14430_body_class_pages( $body_class )
{
if ( is_page() ) {
$null = null;
$top_page = get_post( $null );
$ancestors = get_post_ancestors( $top_page );
if ( $ancestors ) {
$top_page = get_post( array_pop( $ancestors ) );
}
$body_class[] = 'wpse14430_' . $top_page->post_name;
}
return $body_class;
}