Minor css-change based on topmenu – how?

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;
}

Leave a Comment