How to add odd and even classes to all nav links through walker?

I checked out the walk method from the original Walker class source at WordPress and used that to get my result. I added a couple of more things to it, including:

  • Adding first and last classes to the every ul in the navigational menu.
  • Adding odd and even classes to every li relative to the position it is in its ul.
  • Adding before-parent and after-parent classes to every li that precedes or follows a parent, even if the element itself is a parent.

Here’s the code(just add this to your walker class):

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

I also shared my entire detailed navigational menu walker on Github if you wish to use it.