Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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>
share|improve this question
1  
what's wrong with javascript use? is it core functionality they are for? Or could it be seen as a progressive enhancement? –  Mild Fuzz Sep 13 '10 at 18:29

9 Answers 9

up vote 10 down vote accepted

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');
share|improve this answer
    
Nice and simple. I like it! –  Jonathan Wold Jan 8 '12 at 18:17
    
I see this code works for custom nav menus (I saw it work in a custom menu widget), but it's not working in the same menu being used in the primary nav bar (tested in Twenty Eleven). Should it be? or would that be different code? or just a different filter? Thanks! –  Evan Mattson Feb 12 '12 at 6:02
2  
This will work for single level menu, but might not for more complex one because at this point it's array with all items, not top level ones. –  Rarst Aug 26 '12 at 18:08

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.

share|improve this answer

Learn more about the new Menus API in wordpress 3. You can give any element it's own class manually. Also, once mastered, it makes menus a thing of joy to edit.

share|improve this answer
2  
This would be the way to go, simply bring up the nav menu screen in the admin and add a class to the first and last items. Of course if the user moves these menu items, the classes will have to be re-assigned, but this i feel is the most valid/best answer (because there's no coding involved). –  t31os Nov 9 '10 at 17:20

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.

share|improve this answer

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.

share|improve this answer
    
I'm using that, because last-child is now supported by IE 9 too, and I don't care anymore so much about ie 8, 7.. –  Alex Sep 24 '12 at 10:27

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 );
share|improve this answer
1  
exactly what i was looking for. thanks. it's kind of surprising wp_nav_menu doesn't do this automatically –  yitwail Nov 1 '12 at 2:43
    
I agree. I believe there was a feature request in the Wordpress bugtracker for a while, but nothing came of it. At least the proper filters exist so we can add these classes ;). –  Chaoix Nov 7 '12 at 19:10
1  
Thanks. Was looking for the counter, which could be used in the menu walker. your answer allows this. Just added $item->number = $i; and got it in the walker. Thanks!!! –  BasTaller Mar 4 at 19:54

How about:

 ul li:last-child{
     // do something with the last li
 }

and maybe some http://selectivizr.com/

share|improve this answer
    
This would style the first <li> in every unordered list on the site, not just in the menu. It's also substantively identical to this answer wordpress.stackexchange.com/a/63128/9844 –  mrwweb Sep 24 '12 at 16:16
    
Sorry, pseudo code. –  a.m. Sep 24 '12 at 16:55

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.

share|improve this answer

Pure CSS, works for me. This will work with submenus also

ul.nav>li:last-of-type a
share|improve this answer
    
only in IE9 and up, which rules it out for pretty much everything I do :) –  Milo Jun 6 '13 at 18:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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