Adding first / last CSS classes to menus

is that possible, without javascript hacks?
like this:

<ul class="my_menu">
  <li class="first"> ... </li>
  <li> ... </li>
  <li> ... </li>
  <li class"with_sub"> ... 
    <ul class="my_menu_sub">
      <li class="first"> ... </li>
      <li> ... </li>
      <li> ... </li>
      <li class="last"> ... </li>
    </ul>
  </li>
  <li> ... </li>
  <li> ... </li>
  <li class="last"> ... </li>
</ul>

Related posts

Leave a Reply

9 comments

  1. A better and simpler approach:

    function add_first_and_last($items) {
      $items[1]->classes[] = 'first-menu-item';
      $items[count($items)]->classes[] = 'last-menu-item';
      return $items;
    }
    
    add_filter('wp_nav_menu_objects', 'add_first_and_last');
    
  2. Here is rough snippet that takes care of modifying menu output and adding first/last to first and last class (outer ul is not applied at this stage so doesn’t count). Note – requires PHP5 for strripos()

    add_filter( 'wp_nav_menu_items', 'first_last_class' );
    
    function first_last_class( $items ) {
    
        $first = strpos( $items, 'class=' );
    
        if( false !== $first )
             $items = substr_replace( $items, 'first ', $first+7, 0 );
    
        $last = strripos( $items, 'class=');
    
        if( false !== $last )
             $items = substr_replace( $items, 'last ', $last+7, 0 );
    
        return $items;
    }
    

    I am bit stuck with how to make it handle nested lists, but it should get you started at least.

  3. Here is a function for only adding the first/last classes to the parent menu items. For most CSS styling, this is all that is necessary.

    function nav_menu_add_classes( $items, $args ) {
        //Add first item class
        $items[1]->classes[] = 'menu-item-first';
    
        //Add last item class
        $i = count($items);
        while($items[$i]->menu_item_parent != 0 && $i > 0) {
            $i--;
        }
        $items[$i]->classes[] = 'menu-item-last';
    
        return $items;
    }
    add_filter( 'wp_nav_menu_objects', 'nav_menu_add_classes', 10, 2 );
    
  4. If you have nested menus

    function add_first_and_last($items) {
        // first class on parent most level
        $items[1]->classes[] = 'first';
        // separate parents and children
        $parents = $children = array();
        foreach($items as $k => $item){
            if($item->menu_item_parent == '0'){
                $parents[] = $k;
            } else {
                $children[$item->menu_item_parent] = $k;
            }
        }
        // last class on parent most level
        $last = end(array_keys($parents));
        foreach ($parents as $k => $parent) {
            if ($k == $last) {
                $items[$parent]->classes[] = 'last';
            }
        }
        // last class on children levels
        foreach($children as $child){
            $items[$child]->classes[] = 'last';
        }
        // first class on children levels
        $r_items = array_reverse($items, true);
        foreach($r_items as $k => $item){
            if($item->menu_item_parent !== '0'){
                $children[$item->menu_item_parent] = $k;
            }
        }
        foreach($children as $child){
            $items[$child]->classes[] = 'first';
        }
        return $items;
    }
    add_filter('wp_nav_menu_objects', 'add_first_and_last');
    

    I like the simplicity of Ismaelj’s answer, but there needs to be more if you want sub-menu classes.

  5. If you don’t need support for IE8 or lower, don’t forget that you can also use pure CSS:

    .my_menu > :first-child,
    .my_menu > :last-child {
        /* some styles */
    }
    

    jQuery browser support is even better, but it sounds like you’re trying to avoid that.

  6. Here is some better code for adding first and last menu item classes that includes support for nested submenus.

    add_filter( 'wp_nav_menu_objects', 'tgm_filter_menu_class', 10, 2 );
    /**
     * Filters the first and last nav menu objects in your menus
     * to add custom classes.
     *
     * This also supports nested menus.
     *
     * @since 1.0.0
     *
     * @param array $objects An array of nav menu objects
     * @param object $args Nav menu object args
     * @return object $objects Amended array of nav menu objects with new class
     */
    function tgm_filter_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[end( array_keys( $top_ids ) )]->classes[] = 'last-menu-item';
    
        // Return the menu objects
        return $objects;
    
    }
    

    You can find the gist here and the associated tutorial here.