Agregar primera / última clase de CSS a los menús

15

¿Es eso posible, sin jacks javascript? Me gusta esto:

<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>
Alex
fuente
1
¿Qué hay de malo con el uso de JavaScript? ¿Para qué son la funcionalidad principal? ¿O podría verse como una mejora progresiva?
Mild Fuzz

Respuestas:

14

Un enfoque mejor y más simple:

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');
Ismaelj
fuente
1
Agradable y simple ¡Me gusta!
Jonathan Wold
Veo que este código funciona para menús de navegación personalizados (lo vi funcionar en un widget de menú personalizado), pero no funciona en el mismo menú que se utiliza en la barra de navegación primaria (probado en Twenty Eleven). ¿Deberia ser? o sería un código diferente? o simplemente un filtro diferente? ¡Gracias!
Evan Mattson
2
Esto funcionará para el menú de un solo nivel, pero podría no serlo para uno más complejo porque en este punto es una matriz con todos los elementos, no los de nivel superior.
Rarst
6

Aquí hay un fragmento aproximado que se encarga de modificar la salida del menú y agregar primero / último a la primera y última clase (el exterior ulno se aplica en esta etapa, por lo que no cuenta). Nota: requiere PHP5 parastrripos()

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

Estoy un poco atascado con cómo hacer que maneje listas anidadas, pero al menos debería ayudarlo a comenzar.

Rarst
fuente
6

Aquí hay una función para agregar solo las primeras / últimas clases a los elementos del menú principal. Para la mayoría de los estilos CSS, esto es todo lo que se necesita.

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 );
Chaoix
fuente
1
Exactamente lo que estaba buscando. Gracias. es sorprendente wp_nav_menu no hace esto automáticamente
yitwail
Estoy de acuerdo. Creo que hubo una solicitud de función en el rastreador de errores de Wordpress por un tiempo, pero no surgió nada. Al menos existen los filtros adecuados para que podamos agregar estas clases;).
Chaoix
1
Gracias. Estaba buscando el mostrador, que podría usarse en el andador de menús. Su respuesta lo permite. Acabo de agregar $ item-> number = $ i; y lo conseguí en el andador. ¡¡¡Gracias!!!
BasTaller
5

Obtenga más información sobre la nueva API de menús en wordpress 3. Puede asignar manualmente cualquier elemento a su propia clase. Además, una vez dominado, hace que los menús sean una alegría para editar.

Fuzz suave
fuente
2
Este sería el camino a seguir, simplemente abra la pantalla del menú de navegación en el administrador y agregue una clase al primer y último elemento. Por supuesto, si el usuario mueve estos elementos del menú, las clases tendrán que reasignarse, pero creo que esta es la respuesta más válida / mejor (porque no hay codificación involucrada).
t31os
5

Si tienes menús anidados

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');

Me gusta la simplicidad de la respuesta de Ismaelj, pero debe haber más si quieres clases de submenú.

brent
fuente
2

Si no necesita soporte para IE8 o inferior, no olvide que también puede usar CSS puro:

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

La compatibilidad con el navegador jQuery es aún mejor, pero parece que estás tratando de evitar eso.

mrwweb
fuente
Estoy usando eso, porque el último hijo ahora también es compatible con IE 9, y ya no me importa tanto, es decir, 8, 7 ..
Alex
2

Aquí hay un código mejor para agregar las primeras y últimas clases de elementos de menú que incluye soporte para submenús anidados.

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;

}

Puede encontrar la esencia aquí y el tutorial asociado aquí .

Thomas
fuente
0

CSS puro, funciona para mí. Esto funcionará con submenús también

ul.nav>li:last-of-type a
Dan Green-Leipciger
fuente
sólo en IE9 y hasta que lo descarta para casi todo lo que hago :)
Milo