如何通过Walker为所有的导航链接添加奇偶类?

时间:2016-05-29 作者:yaserso

我一直在寻找解释如何将偶数类和奇数类添加到子菜单项的帖子,但似乎找不到如何将其添加到没有子菜单的主菜单项。

我已经查看了以下链接:

  • http://shinraholdings.com/62/custom-nav-menu-walker-function/
  • https://css-tricks.com/snippets/php/applying-evenodd-classes/
  • https://developer.wordpress.org/reference/functions/wp_nav_menu/#comment-207
  • Customizing a walker menu class
  • https://wordpress.org/support/topic/odd-class-to-odd-lis-within-sub-menu-uls
  • https://wordpress.org/support/topic/add-page-id-to-walker-function-start_lvl
  • https://stackoverflow.com/questions/18354343/add-odd-class-to-odd-lis-within-sub-menu-uls-using-custom-walker
  • Custom Walker: how to get ID in function start_lvl
  • Custom nav walker with different output depending on depth
  • Customizing a walker menu class

    <?php
    
    if ( ! defined( \'ABSPATH\' ) ) exit; // Exit if accessed directly
    
    class detailed_walker_nav_menu extends Walker_Nav_Menu {
    
      // Add First & Last Classes To Navigational Menu Objects
      static function first_last_menu_class( $objects, $args ) {
    
        // Add first/last classes to nested menu items
        $ids        = array();
        $parent_ids = array();
        $top_ids    = array();
        foreach ( $objects as $i => $object ) {
          // If there is no menu item parent, store the ID and skip over the object
          if ( 0 == $object->menu_item_parent ) {
            $top_ids[$i] = $object;
            continue;
          }
    
          // Add first item class to nested menus
          if ( ! in_array( $object->menu_item_parent, $ids ) ) {
            $objects[$i]->classes[] = \'first-menu-item\';
            $ids[] = $object->menu_item_parent;
          }
    
          // If we have just added the first menu item class, skip over adding the ID
          if ( in_array( \'first-menu-item\', $object->classes ) )
            continue;
    
          // Store the menu parent IDs in an array
          $parent_ids[$i] = $object->menu_item_parent;
        }
    
        // Remove any duplicate values and pull out the last menu item
        $sanitized_parent_ids = array_unique( array_reverse( $parent_ids, true ) );
    
        // Loop through the IDs and add the last menu item class to the appropriate objects
        foreach ( $sanitized_parent_ids as $i => $id )
          $objects[$i]->classes[] = \'last-menu-item\';
    
        // Finish it off by adding classes to the top level menu items
        $objects[1]->classes[] = \'first-menu-item\'; // We can be assured 1 will be the first item in the menu :-)
        $objects[array_keys( $top_ids )[count( array_keys( $top_ids ) ) - 1]]->classes[] = \'last-menu-item\';
    
        // Return the menu objects
        return $objects;
    
      }
    
      // add classes to ul sub-menus
      function start_lvl( &$output, $depth = 0, $args = array() ) {
        // depth dependent classes
        $indent = ( $depth > 0  ? str_repeat( "\\t", $depth ) : \'\' ); // code indent
        $display_depth = ( $depth + 1 ); // because it counts the first submenu as 0
        $classes = array(
          \'sub-menu toggleable\',
          ( $display_depth % 2  ? \'menu-odd\' : \'menu-even\' ),
          ( $display_depth >=2 ? \'sub-sub-menu\' : \'\' ),
          \'menu-depth-\' . $display_depth
        );
        $class_names = implode( \' \', $classes );
    
        // build html
        $output .= "\\n" . $indent . \'<ul class="\' . $class_names . \'">\' . "\\n";
      }
    
      // add main/sub classes to li\'s and links
      function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        $indent = ( $depth > 0 ? str_repeat( "\\t", $depth ) : \'\' ); // code indent
    
        // depth dependent classes
        $depth_classes = array(
          ( $depth == 0 ? \'main-menu-item\' : \'sub-menu-item\' ),
          ( $depth >=2 ? \'sub-sub-menu-item\' : \'\' ),
          ( $depth % 2 ? \'menu-item-odd\' : \'menu-item-even\' ),
          \'menu-item-depth-\' . $depth
        );
        $depth_class_names = esc_attr( implode( \' \', $depth_classes ) );
    
        // passed classes
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $class_names = esc_attr( implode( \' \', apply_filters( \'nav_menu_css_class\', array_filter( $classes ), $item ) ) );
    
        // build html
        $output .= $indent . \'<li id="nav-menu-item-\'. $item->ID . \'" class="\' . $depth_class_names . \' \' . $class_names . \'">\';
    
        // link attributes
        $attributes  = ! empty( $item->attr_title ) ? \' title="\'  . esc_attr( $item->attr_title ) .\'"\' : \'\';
        $attributes .= ! empty( $item->target )     ? \' target="\' . esc_attr( $item->target     ) .\'"\' : \'\';
        $attributes .= ! empty( $item->xfn )        ? \' rel="\'    . esc_attr( $item->xfn        ) .\'"\' : \'\';
        $attributes .= ! empty( $item->url )        ? \' href="\'   . esc_attr( $item->url        ) .\'"\' : \'\';
        $attributes .= \' class="menu-link \' . ( $depth > 0 ? \'sub-menu-link\' : \'main-menu-link\' ) . \'"\';
    
        if ( $item->hasChildren ) {
          $args->after = sprintf( \'<input type="checkbox" id="%1$s-%2$s-checkbox" hidden><label class="toggler" for="%1$s-%2$s-checkbox" onclick><i class="fa fa-lg fa-caret-down"></i></label>\', $item->ID, $args->theme_location );
        } else {
          $args->after = null;
        }
    
        $item_output = sprintf( \'%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s\',
          $args->before,
          $attributes,
          $args->link_before,
          apply_filters( \'the_title\', $item->title, $item->ID ),
          $args->link_after,
          $args->after
        );
    
        // build html
        $output .= apply_filters( \'walker_nav_menu_start_el\', $item_output, $item, $depth, $args, $id );
      }
      function display_element( $element, &$children_elements, $max_depth, $depth = 0, $args, &$output ) {
        // check, whether there are children for the given ID and append it to the element with a (new) ID
        $element->hasChildren = isset( $children_elements[$element->ID] ) && !empty( $children_elements[$element->ID] );
    
        return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
      }
    }
    ?>
    

1 个回复
最合适的回答,由SO网友:yaserso 整理而成

我查看了walk method from the original Walker class source at WordPress 然后用它来得到我的结果。我还添加了一些内容,包括:

添加firstlast 每节课ul 在导航菜单中odd 和even 课程到每个li 相对于其所在位置ul.before-parent 和after-parent 课程到每个li 位于父元素之前或之后,即使元素本身是父元素以下是代码(只需将此添加到walker类中):

/**
 * Traverse elements to create list from elements.
 *
 * Display one element if the element doesn\'t have any children otherwise,
 * display the element and its children. Will only traverse up to the max
 * depth and no ignore elements under that depth. It is possible to set the
 * max depth to include all depths, see walk() method.
 */
public function display_element($element, &$children_elements, $max_depth, $depth = 0, $args, &$output) {
  $element->is_subitem = ((!empty($children_elements[$element->ID]) && (($depth + 1) < $max_depth || ($max_depth === 0))));

  if ($element->is_subitem) {
    foreach ($children_elements[$element->ID] as $child) {
      if ($child->current_item_parent || $this->url_compare($this->archive, $child->url)) {
        $element->classes[] = \'active\';
      }
    }
  }

  $element->is_active = (!empty($element->url) && strpos($this->archive, $element->url));

  if ($element->is_active) {
    $element->classes[] = \'active\';
  }

  parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
}

/*
 * Display array of elements hierarchically.
 * Does not assume any existing order of elements.
 * $max_depth = -1 means flatly display every element.
 * $max_depth = 0 means display all levels.
 * $max_depth > 0 specifies the number of display levels.
 */
public function walk( $elements, $max_depth ) {
  $args = array_slice( func_get_args(), 2 );
  $output = \'\';

  // Invalid parameter or nothing to walk.
  if ( $max_depth < -1 || empty( $elements ) ) {
    return $output;
  }

  $parent_field = $this->db_fields[\'parent\'];

  // Flat display.
  if ( -1 == $max_depth ) {
    $empty_array = array();
    foreach ( $elements as $e )
      $this->display_element( $e, $empty_array, 1, 0, $args, $output );
    return $output;
  }

  /*
   * Need to display in hierarchical order.
   * Separate elements into two buckets: top level and children elements.
   * Children_elements is two dimensional array, eg.
   * Children_elements[10][] contains all sub-elements whose parent is 10.
   */
  $top_level_elements = array();
  $children_elements  = array();
  foreach ( $elements as $e ) {
    if ( empty( $e->$parent_field ) )
      $top_level_elements[] = $e;
    else
      $children_elements[ $e->$parent_field ][] = $e;
  }

  /*
   * When none of the elements is top level.
   * Assume the first one must be root of the sub elements.
   */
  if ( empty( $top_level_elements ) ) {

    $first = array_slice( $elements, 0, 1 );
    $root = $first[0];

    $top_level_elements = array();
    $children_elements  = array();
    foreach ( $elements as $e ) {
      if ( $root->$parent_field == $e->$parent_field )
        $top_level_elements[] = $e;
      else
        $children_elements[ $e->$parent_field ][] = $e;
    }
  }

  /*
   * Add number of positions per hierarchy using arrays from earlier at the top of the function.
   * One for top level elements and the other for child elements.
   */
  foreach ( $top_level_elements as $i => $e ) {                                     // Loop to add classes to top level elements loop.
    array_push( $e->classes, ( $i+1 ) % 2  ? \'odd\' : \'even\' );                      // Add odd and even classes based on position.

    // Add [before | after]-parent classes to element.
    if ($i <> count( $top_level_elements ) - 1 ) {                                  // If it is not the last element.
      if (array_key_exists($top_level_elements[$i + 1 ]->ID, $children_elements)) { // If next element is a parent.
        array_push($e->classes, \'before-parent\');                                   // Add before-parent class.
      }
    }
    if ($i <> 0) {                                                                  // If it is not the first element.
      if (array_key_exists($top_level_elements[$i - 1 ]->ID, $children_elements)) { // If previous element is a parent.
        array_push($e->classes, \'after-parent\');                                    // Add after-parent class.
      }
    }

    // Add first and last classes to items.
    if ( $i == 0 ) {
      array_push( $e->classes, \'first\' );                                           // Add first class to first item.
    } elseif ( $i == ( count( $top_level_elements ) - 1 ) ) {
      array_push( $e->classes, \'last\' );                                            // Add last class to last item.
    }
  }

  foreach ( $children_elements as $children ) {                                     // Loop to add classes to child level elements loop.
    foreach ( $children as $i => $e ) {
      array_push( $e->classes, ( $i+1 ) % 2  ? \'odd\' : \'even\' );                    // Add odd and even classes based on position.

      // Add [before | after]-parent classes to element.
      if ($i <> count( $children ) - 1 ) {                                          // If it is not the last element.
        if (array_key_exists($children[$i + 1 ]->ID, $children_elements)) {         // If next element is a parent.
          array_push($e->classes, \'before-parent\');                                 // Add before-parent class.
        }
      }
      if ($i <> 0) {                                                                // If it is not the first element.
        if (array_key_exists($children[$i - 1 ]->ID, $children_elements)) {         // If previous element is a parent.
          array_push($e->classes, \'after-parent\');                                  // Add after-parent class.
        }
      }

      if ( $i == 0 ) {
        array_push( $e->classes, \'first\' );                                         // Add first class to first item.
      }if ( $i == ( count( $children ) - 1 ) ) {
        array_push( $e->classes, \'last\' );                                          // Add last class to last item.
      }
    }
  }

  foreach ( $top_level_elements as $e )
    $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );

  /*
   * If we are displaying all levels, and remaining children_elements is not empty,
   * then we got orphans, which should be displayed regardless.
   */
  if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
    $empty_array = array();
    foreach ( $children_elements as $orphans )
      foreach ( $orphans as $op )
        $this->display_element( $op, $empty_array, 1, 0, $args, $output );
  }

  return $output;
}
我还分享了我的整个detailed navigational menu walker 如果您希望使用它,请访问Github。

相关推荐

WordPress中声明SplitMenuWalker::Walk($Elements,$max_Depth)时出现警告

我开始在WordPress网站上收到以下错误:警告:SplitMenuWalker::walk($elements,$max\\u depth)的声明应与/home/relati67/public\\u html/wp content/themes/mentis/inc/mega menu/split menu中的walk::walk($elements,$max\\u depth,$args)兼容。php第0行我不知道在这里该怎么办。我发现了一个具有类似内容的线程,但错误指向特定行(不是第0行),并且不