¿Descripción de elementos del menú? Walker personalizado para wp_nav_menu()

6 abr 2011, 01:25:50
Vistas: 113K
Votos: 111

El menú normal de WordPress se ve así:

Inicio | Blog | Sobre nosotros | Contacto

Pero he visto muchas páginas con descripciones debajo de estos enlaces:

Página de inicio | Nuestros Blogs | Sobre nosotros | Contacto
...conócenos... | leer más | información básica | formulario de contacto

¿Cómo se puede lograr esto?

(Quiero que sea una función principal para todos mis temas, así que no necesito plugins, solo quiero saber cómo se hace)

0
Todas las respuestas a la pregunta 4
5
126

Necesitas un walker personalizado para el menú de navegación.

Básicamente, agregas un parámetro 'walker' a las opciones de wp_nav_menu() y llamas a una instancia de una clase mejorada:

wp_nav_menu(
    array (
        'menu'            => 'main-menu',
        'container'       => FALSE,
        'container_id'    => FALSE,
        'menu_class'      => '',
        'menu_id'         => FALSE,
        'depth'           => 1,
        'walker'          => new Description_Walker
    )
);

La clase Description_Walker extiende Walker_Nav_Menu y modifica la función start_el( &$output, $item, $depth, $args ) para buscar $item->description.

Un ejemplo básico:

/**
 * Crea una lista HTML de elementos del menú de navegación.
 * Reemplazo para el Walker nativo, utilizando la descripción.
 *
 * @see    https://wordpress.stackexchange.com/q/14037/
 * @author fuxia
 */
class Description_Walker extends Walker_Nav_Menu
{
    /**
     * Inicia la salida del elemento.
     *
     * @param  string $output Pasado por referencia. Se utiliza para añadir contenido adicional.
     * @param  object $item   Objeto de datos del elemento del menú.
     * @param  int $depth     Profundidad del elemento del menú. Puede usarse para relleno.
     * @param  array|object $args    Cadenas adicionales. En realidad siempre una 
                                     instancia de stdClass. Pero esto es WordPress.
     * @return void
     */
    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 )
    {
        $classes     = empty ( $item->classes ) ? array () : (array) $item->classes;

        $class_names = join(
            ' '
        ,   apply_filters(
                'nav_menu_css_class'
            ,   array_filter( $classes ), $item
            )
        );

        ! empty ( $class_names )
            and $class_names = ' class="'. esc_attr( $class_names ) . '"';

        $output .= "<li id='menu-item-$item->ID' $class_names>";

        $attributes  = '';

        ! empty( $item->attr_title )
            and $attributes .= ' title="'  . esc_attr( $item->attr_title ) .'"';
        ! empty( $item->target )
            and $attributes .= ' target="' . esc_attr( $item->target     ) .'"';
        ! empty( $item->xfn )
            and $attributes .= ' rel="'    . esc_attr( $item->xfn        ) .'"';
        ! empty( $item->url )
            and $attributes .= ' href="'   . esc_attr( $item->url        ) .'"';

        // inserta la descripción solo para elementos de nivel superior
        // puedes cambiar esto
        $description = ( ! empty ( $item->description ) and 0 == $depth )
            ? '<small class="nav_desc">' . esc_attr( $item->description ) . '</small>' : '';

        $title = apply_filters( 'the_title', $item->title, $item->ID );

        $item_output = $args->before
            . "<a $attributes>"
            . $args->link_before
            . $title
            . '</a> '
            . $args->link_after
            . $description
            . $args->after;

        // Como $output se pasa por referencia, no necesitamos devolver nada.
        $output .= apply_filters(
            'walker_nav_menu_start_el'
        ,   $item_output
        ,   $item
        ,   $depth
        ,   $args
        );
    }
}

O, alternativamente como comentó @nevvermind, podrías heredar todas las funcionalidades de la función start_el del padre y simplemente añadir la descripción a $output:

function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) 
{
    parent::start_el( $output, $item, $depth, $args );
    $output .= sprintf( 
        '<i>%s</i>', 
        esc_html( $item->description ) 
    );
}

Ejemplo de salida:

Ejemplo de salida del menú con descripción

Ahora habilita el campo de descripción en wp-admin/nav-menus.php para tener la posibilidad de editar este campo. Si no lo haces, WP simplemente volcará todo el contenido de tu publicación en él.

Habilitar campo de descripción en el administrador de menús

Lecturas adicionales:

Y eso es todo.

6 abr 2011 03:15:46
Comentarios

Si para ti herencia != reescribir todo el método, solo mantener el mismo nombre, prueba esto: public function start_el(&$output, $item, $depth, $args) { parent::start_el($output, $item, $depth, $args); $output .= sprintf('<i>%s</i>', esc_html($item->description)); }

nevvermind nevvermind
14 dic 2011 15:15:57

@nevvermind Deberías al menos verificar si la descripción tiene algún contenido. ;) La posición de la descripción en mi código de ejemplo es solo la forma más simple de ilustrar la solución. Si necesitas colocar la descripción dentro del enlace, tendrás que reconstruir toda la función.

fuxia fuxia
16 dic 2011 12:38:44

sí, tendrías que escribir el método completo, sin duda, pero para las personas que necesitan (digamos...) añadirlo, esto podría ahorrarles muchos dolores de cabeza. ¡Y todo esto es culpa de WP! ¡Arggg!

nevvermind nevvermind
16 dic 2011 16:47:54

Buen trabajo y lo he usado en esta respuesta modificándolo un poco, tal vez puedas mejorarlo si me faltó algo, gracias.

The Alpha The Alpha
28 ago 2012 17:24:30

Lo que realmente necesitaba era el wp_nav_menu, pero necesitaba cambiar el parámetro 'container_class', para que funcionara en mi caso particular, donde bajo ciertas condiciones reemplazaba el menú principal por otro, pero necesitaba que las clases fueran consistentes para el css.

D. Dan D. Dan
25 ene 2018 17:15:25
1
42

Desde WordPress 3.0, ¡ya no necesitas un walker personalizado!

Existe el filtro walker_nav_menu_start_el, consulta https://developer.wordpress.org/reference/hooks/walker_nav_menu_start_el/

Ejemplo:

function add_description_to_menu($item_output, $item, $depth, $args) {

   if (strlen($item->description) > 0 ) {
      // agregar descripción después del enlace
      $item_output .= sprintf('<span class="description">%s</span>', esc_html($item->description));
    
      // o.. insertar descripción como último elemento dentro del enlace ($item_output termina con "</a>{$args->after}")
      // $item_output = substr($item_output, 0, -strlen("</a>{$args->after}")) . sprintf('<span class="description">%s</span >', esc_html($item->description)) . "</a>{$args->after}";
   }   
   return $item_output;
}
add_filter('walker_nav_menu_start_el', 'add_description_to_menu', 10, 4);
23 feb 2015 17:31:03
Comentarios

¡Genial! Estaba usando la solución nav walker de @toscho, pero esta es mucho más limpia y fácil de mantener. Esta debería ser la respuesta aceptada, una práctica mucho mejor.

Ronaldt Ronaldt
8 jul 2015 13:29:04
0

Esto no es mejor ni peor que otras sugerencias; simplemente es diferente. Además, es corto y sencillo.

En lugar de usar el campo de descripción como sugiere @toscho, podrías completar el campo "Título" en cada elemento del menú con el texto que deseas, y luego usar este CSS:

.menu-item a:after { content: attr(title); }

También sería fácil usar jQuery para añadirlo, pero como el texto es meramente ornamental, CSS parece más apropiado.

18 abr 2012 08:52:12
1

También puedes escribir un elemento <span> después de la etiqueta de navegación en los menús y usar la siguiente regla CSS para cambiar su propiedad display (que por defecto es inline):

span {display:block}
8 sept 2011 11:58:03
Comentarios

Bueno, es una solución simple y fácil, pero ¿por qué usar span si lo conviertes en bloque de todos modos? xhtml/html4 no permite elementos de bloque dentro de enlaces, pero html5 sí lo permite, así que simplemente usa div, ¡y no necesitas ningún css!

James Mitch James Mitch
5 mar 2013 23:02:16