How to add current, parent, and ancestor menu item IDs to body_class()?

(just got reccommended to post this over here from stack overflow…)

I am just starting with WordPress (defecting from Joomla)- having a bit of difficulty getting started.

Read More

One thing I am trying to do is make a custom plugin that allows the user to add custom backgrounds to the site, based on the active menu item.

So what I am trying to do is get the active menu id (like ‘menu-item-xx’ that is added to the nav li’s) and active parent menu id so I can add these to the body classes.

Is this something I can get from anything built into WordPress or is this something I will need to make a custom for?

Any pointers most welcome…

UPDATE

I have solved this now with a custom function that you can find below

Related posts

Leave a Reply

4 comments

  1. From my comment earlier: Use the body_class() function in your header.php file or wherever your body tag is, e.g. <body <?php body_class(); ?>>. This will give you an output with a bunch of classes on the body that you can then use in your CSS. For example, <body class="page page-id-114 page-parent page-template-default logged-in admin-bar">. You can also use the body_class filter to add more classes. If you want to add the slug, for instance, you would global $post and then use $post->post_name.

  2. Ok I have sussed out a function to achieve this. I am at the very start of my journey with WordPress so I am not sure about the quality or efficiency of this code. But here it is:-

    function get_active_menu_item_ids( $classes )
    {   
    //set up defaults for menu retrieval
    $dosmenudefaults = array( 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '',
    'echo' => true, 'fallback_cb' => 'wp_page_menu', 'before' => '', 'after' => '', 'link_before' => '', 'link_after' => '', 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
    'depth' => 0, 'walker' => '', 'theme_location' => '' );
    
    $dosargs = wp_parse_args( $dosargs, $dosmenudefaults );
    $dosargs = apply_filters( 'wp_nav_menu_args', $dosargs );
    $dosargs = (object) $dosargs;
    
    // Get the nav menu based on the requested menu
    $dosmenu = wp_get_nav_menu_object( $dosargs->menu );
    
    // Get the nav menu based on the theme_location
    if ( ! $dosmenu && $dosargs->theme_location && ( $doslocations = get_nav_menu_locations() ) && isset( $doslocations[ $dosargs->theme_location ] ) )
        $dosmenu = wp_get_nav_menu_object( $doslocations[ $dosargs->theme_location ] );
    
    // Get the first menu that has items if we still can't find a menu
    if ( ! $dosmenu && !$dosargs->theme_location ) {
        $dosmenus = wp_get_nav_menus();
        foreach ( $dosmenus as $dosmenu_maybe ) {
            if ( $dosmenu_items = wp_get_nav_menu_items($dosmenu_maybe->term_id) ) {
                $dosmenu = $dosmenu_maybe;
                break;
            }
        }
    }
    
    // If the menu exists, get its items.
    if ( $dosmenu && ! is_wp_error($dosmenu) && !isset($dosmenu_items) )
        $dosmenu_items = wp_get_nav_menu_items( $dosmenu->term_id );    
    $dosmenu = $dosmenu_items;
    
    // Get the $menu_item variables
     _wp_menu_item_classes_by_context( $dosmenu );
    
     //create empty parents array
     $dosparents = array();
    
     //Iterate through the menu items and get the active item and its parent items
     foreach ($dosmenu as $dosmenuitem)
        {
        if ($dosmenuitem->current == '1')
            {
            $dosactivemenuids['current']= $dosmenuitem->ID;
            }
        if (($dosmenuitem->current_item_parent == '1')||($dosmenuitem->current_item_ancestor == '1'))
            {
            $dosparents[$dosmenuitem->menu_order] = $dosmenuitem->ID;
            }
        if(is_array($dosparents))
            {
            krsort($dosparents);
            foreach ($dosparents as $key =>$value)
                {
                $dosactivemenuids['parents'][$key]= $value;
                }
            }
         }
    //return $dosactivemenuids;
    if (is_array($dosactivemenuids['parents']))
        {
        foreach ( $dosactivemenuids['parents'] as $key=>$value)
            {
            $classes[] = 'menu-item-'.$value;
            }
        }
    if ($dosactivemenuids['current'] != '')
        {
        $classes[] = 'menu-item-'.$dosactivemenuids['current'];
        }
    
    return $classes;
    }
    

    You add that to your templates ‘functions.php’ and also somewhere after the function add a filter to call it with:-

    add_filter( 'body_class', 'get_active_menu_item_ids');
    

    What this does?

    What this does is get the active menu ids and adds them to the body classes in ascending order

    Why would I need this?

    Coming from a CMS background I am used to pages being defined by the menu. With this function the active menu item id and/or its parent item id is added to the body classes so you can add page wide styles based on the active menu item. So for example you may have a menu structure like this:-

    ->menu-item-1

    ->menu-item-2

    ->->menu-item-3

    Using this function if you are viewing the page ‘menu-item-3’ the classes ‘menu-item-2’ and ‘menu-item-3’ are added to the body tag in that order. What this means is that you can have a css rule that targets body.menu-item-3 that can fall back to its parent items rule in body.menu-item-2 and ultimately to body if there are no styles for either. What I am using this for, for example, is to have a default background colour for the whole page (body) a background colour for the whole page when menu-item-2 is active (body.menu-item-2) and a different background colour for the page when menu-item-3 is active (body.menu-item-3).

    Hope that helps someone 😉

  3. I have tried getting the Page slug but it seems to be empty in wp_head but you can get the page id and assign it to the body class like so:

    add_action('wp_head', 'bodyclassReplacer');
    function bodyclassReplacer() {
    
        global $wp_query;
        $thePostID = $wp_query->post->ID;
        echo str_replace('<body>', '<body class="page'. $thePostID .'">', '<body>');
    }
    

    .
    After you got a class you can assign a background using css like this:

    body.page6 {
        background:url("images/background6.png") no-repeat;
    }
    

    .
    ps. it was fun finding the solution 🙂 – hope this helps,
    cheers, Sagive

    .
    As for Dynamic Menu Highlighting- here is a code that would do that:

    add_filter( 'nav_menu_css_class', 'additional_active_item_classes', 10, 2 );
    
    function additional_active_item_classes($classes = array(), $menu_item = false){
    
        if(in_array('current-menu-item', $menu_item->classes)){
            $classes[] = 'active';
        }
    
        return $classes;
    }
    
  4. Ok my last shot was a while ago now and I was a bit of a novice- I look at that now and its embarassing! I really did not get WordPress then!

    This is what you need…

    function ics_get_active_menu_item_ids(){
    $menus = wp_get_nav_menus();
    $menu_menu_items = array();
    $current_menu_items = array();
    $current_menu_items['parents'] = array();
    $current_menu_items['ancestors'] = array();
    foreach($menus as $menu)
        {
        $menu_menu_items[] = wp_get_nav_menu_items($menu->term_id);
        }
    foreach ($menu_menu_items as $menu_items)
        {
        foreach($menu_items as $menu_item)
            {
            if ($menu_item->current == 1)
                {
                $current_menu_items['current'] = $menu_item->ID;
                }
            if ($menu_item->current_item_parent == 1)
                {
                $current_menu_items['parents'][] = $menu_item->ID;
                }
            if ($menu_item->current_item_ancestor == 1)
                {
                $current_menu_items['ancestors'][] = $menu_item->ID;
                }
            }
        }
        $current_menu_items['parents'] = array_values(array_unique($current_menu_items['parents']));
        $current_menu_items['ancestors'] = array_values(array_unique($current_menu_items['ancestors']));
    return  $current_menu_items;
    }
    

    returns the current item id along with parents and ancestors 😉