3

I would like to add the CSS class of the current menu item (and the classes of its parents & ancestors) to the current page body classes.

For example, if the current menu item has a CSS class "products" (as defined in Appearance | Menus), then the body for the corresponding page should also have the class "products".

If that makes sense to anyone, any ideas how to do it?

I've looked at "How to add current, parent, and ancestor menu item IDs to body_class()?" but can't get those ideas to work in my case.

3 Answers 3

1

This is what I managed to cobble together thanks to Mridul Aggarwal's hints:

function pa_assign_menu_class_to_body(){
  // 'main' is the theme_location, set earlier using register_nav_menus()
  $menu_name = 'main';
  $class_list = array();

  if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
    $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );
    $menu_items = wp_get_nav_menu_items($menu->term_id);

    // _wp_menu_item_classes_by_context() adds current, current_item_parent and current_item_ancestor to the appropriate arrays in the provided variable
    _wp_menu_item_classes_by_context( $menu_items );

    $classes = array();

    foreach($menu_items as $menu_item) {
      if ($menu_item->current == 1) {
        $classes['current'] = $menu_item->classes;
      }
      if ($menu_item->current_item_parent == 1) {
        $classes['parents'] = $menu_item->classes;
      }
      if ($menu_item->current_item_ancestor == 1) {
        $classes['ancestors'] = $menu_item->classes;
      }
    }

    // create a one-dimensional array of unique classes
    foreach($classes as $class) 
      foreach ($class as $cls) $class_list[] = $cls;
    $class_list = array_values(array_unique($class_list));
  }
  // if, for some reason, we have no results, we need to assign a default class otherwise WordPress complains
  if (empty($class_list)) $class_list[] = 'default';
  return $class_list;
}
add_filter( 'body_class', 'pa_assign_menu_class_to_body');

This function does a little too much, because it adds all the menu-item-type classes too. All I really want is the custom CSS classes which the user has entered into the CSS Classes field in the WordPress menus admin screen. Any thoughts on how to extract only those classes would be most welcome.

0

In the answer you linked, do a var_dump($dosmenu); just above the line _wp_menu_item_classes_by_context( $dosmenu );. That variable should be already initialised with the classes you want.

After that you can adapt the foreach loop accordingly & use that function as a filter to body_class.

The classes set in the admin are stored as a post meta with key _menu_item_classes which are automatically initialised in the call to wp_get_nav_menu_items

1
  • Thanks, that gave me sufficient hints to reach a working solution, although it's not perfect. I'll post my function as a separate answer.
    – dvd3141
    Dec 9, 2012 at 21:33
0

Slight alteration to your code posted above to get only custom class. The custom class is always the first item.

function pa_assign_menu_class_to_body(){
  // 'main' is the theme_location, set earlier using register_nav_menus()
  $menu_name = 'main';
  $class_list = array();

  if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
    $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );
    $menu_items = wp_get_nav_menu_items($menu->term_id);

    // _wp_menu_item_classes_by_context() adds current, current_item_parent and current_item_ancestor to the appropriate arrays in the provided variable
    _wp_menu_item_classes_by_context( $menu_items );

    $classes = array();

    foreach($menu_items as $menu_item) {
      if ($menu_item->current == 1) {
        $classes['current'] = $menu_item->classes;
      }
      if ($menu_item->current_item_parent == 1) {
        $classes['parents'] = $menu_item->classes;
      }
      if ($menu_item->current_item_ancestor == 1) {
        $classes['ancestors'] = $menu_item->classes;
      }
    }

    // create a one-dimensional array of unique classes
    foreach($classes as $class) 
      foreach ($class as $cls) $class_list[] = $cls;
    $class_list = array_values(array_unique($class_list));
  }
  // if, for some reason, we have no results, we need to assign a default class otherwise WordPress complains
  if (empty($class_list)) $class_list[] = 'default';
  //Gets first class if custom. If not, returns null.
  if ($class_list[0] != 'menu-item') {
      $class_list_return[] = $class_list[0];
  } else {
      $class_list_return[] = null;
  }
  return $class_list_return;
}
add_filter( 'body_class', 'pa_assign_menu_class_to_body');

EDIT: This and the original filter replace the default classes though. If you just want to add them...

function pa_assign_menu_class_to_body($classes){
  // 'main' is the theme_location, set earlier using register_nav_menus()
  $menu_name = 'main';
  $class_list = array();

  if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
    $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );
    $menu_items = wp_get_nav_menu_items($menu->term_id);

    // _wp_menu_item_classes_by_context() adds current, current_item_parent and current_item_ancestor to the appropriate arrays in the provided variable
    _wp_menu_item_classes_by_context( $menu_items );

    foreach($menu_items as $menu_item) {
      if ($menu_item->current == 1) {
        $classes['current'] = $menu_item->classes;
      }
      if ($menu_item->current_item_parent == 1) {
        $classes['parents'] = $menu_item->classes;
      }
      if ($menu_item->current_item_ancestor == 1) {
        $classes['ancestors'] = $menu_item->classes;
      }
    }

    // create a one-dimensional array of unique classes
    foreach($classes as $class) 
      foreach ($class as $cls) $class_list[] = $cls;
    $class_list = array_values(array_unique($class_list));
  }
  // if, for some reason, we have no results, we need to assign a default class otherwise WordPress complains
  if (empty($class_list)) $class_list[] = 'default';
  //Gets first class if custom. If not, returns null.
  if ($class_list[0] != 'menu-item') {
      $class_list_return[] = $class_list[0];
  } else {
      $class_list_return[] = null;
  }
  return array_merge($classes, $class_list_return);
}
add_filter( 'body_class', 'pa_assign_menu_class_to_body');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.