Update body class based on menu

I’m trying to add a class to the body tag based on whether current menu items either have children, or are children. The custom menu was added via wp_nav_menu.

I have hooked into wp_nav_menu_objects via a filter, and am successfully detecting whether the current active menu item is a ancestor or child:

Read More
function has_submenu( $menu_items ) {

    $current_id = 0;

    foreach( $menu_items as $menu_item ) {

        // Get the id of the current menu item
        if( $menu_item->current ) {
            $current_id = $menu_item->ID;
        }
        // if the current item has a child
        if( $menu_item->menu_item_parent != 0 && $menu_item->menu_item_parent == $current_id ) {
            $body_class = 'has-submenu';
            break;
        }
        // if the current item has an ancestor
        if( $menu_item->current_item_ancestor ) {
            $body_class = 'is-submenu';
            break;
        }
    }
    return $menu_items;
}
add_filter( 'wp_nav_menu_objects', 'has_submenu', 10, 2 );

Now the issue i’m having is how to I then filter body_class to add the class?? I created the variable called $body_class, however i’m not sure hot to pass it to the body_class hook function. Make it a global variable? Create a class for all this?

Thanks in advance!

Related posts

1 comment

  1. As @CharlesClarkson has already explained, you cannot modify the page output with PHP after the output has been sent to the browser. Your menu certainly runs after body_class since the menu must run inside the <body> tag.

    The only PHP solution I see would involve editing your theme templates in addition to the code above (with changes).

    If you run wp_nav_menu before the <body> tag but use output buffering to capture the content instead of print it, what you are trying to do might work.

    You would need to run your menu before the <body> tag, like this:

    ob_start();
    wp_nav_menu();
    $my_captured_menu = ob_get_contents();
    ob_end_clean();
    

    In your function callback, instead of lines like this:

    $body_class = 'has-submenu';
    

    You would need this:

    add_filter(
      'body_class',
      function($classes) {
        $classes[] = 'has-submenu'; // or 'is-submenu'
        return $classes;
      }
    );
    

    And of course, use echo $my_captured_menu wherever it needs to be printed.

Comments are closed.