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

104

El menú normal de Wordpress se ve así:

Inicio | Blog | Acerca de nosotros | Contacto

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

Página de inicio | Nuestros blogs | Acerca de nosotros | Contacto
.... conózcanos ... | leer más | información básica | Formulario de contacto

¿Cómo lograr esto?

(Quiero que sea la función principal para todos mis temas, por lo que no hay complementos, solo quiero saber cómo se hace)

Wordpressor
fuente

Respuestas:

115

Necesita un andador personalizado para el menú de navegación.

Básicamente, agrega un parámetro 'walker'a las wp_nav_menu()opciones y llama 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 se Description_Walkerextiende Walker_Nav_Menuy cambia la función start_el( &$output, $item, $depth, $args )a buscar $item->description.

Un ejemplo básico:

/**
 * Create HTML list of nav menu items.
 * Replacement for the native Walker, using the description.
 *
 * @see    https://wordpress.stackexchange.com/q/14037/
 * @author fuxia
 */
class Description_Walker extends Walker_Nav_Menu
{
    /**
     * Start the element output.
     *
     * @param  string $output Passed by reference. Used to append additional content.
     * @param  object $item   Menu item data object.
     * @param  int $depth     Depth of menu item. May be used for padding.
     * @param  array|object $args    Additional strings. Actually always an 
                                     instance of stdClass. But this is 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        ) .'"';

        // insert description for top level elements only
        // you may change this
        $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;

        // Since $output is called by reference we don't need to return anything.
        $output .= apply_filters(
            'walker_nav_menu_start_el'
        ,   $item_output
        ,   $item
        ,   $depth
        ,   $args
        );
    }
}

O, alternativamente, como comentó @nevvermind , podría heredar todas las funcionalidades de la función de los padres start_ely simplemente agregar 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 ) 
    );
}

Salida de muestra:

ingrese la descripción de la imagen aquí

Ahora habilite el campo de descripción wp-admin/nav-menus.phppara obtener la posibilidad de editar este campo. Si no lo hace, WP simplemente tira a la basura el contenido completo de su publicación.

ingrese la descripción de la imagen aquí

Otras lecturas:

Y eso es.

fuxia
fuente
11
Si por tu herencia! = Reescribe todo el método, solo mantén 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
2
@nevvermind Al menos debe verificar si la descripción tiene algún contenido. ;) La posición de la descripción en mi código de muestra es la forma más sencilla de ilustrar la solución. Si usted necesita para obtener la descripción en el anclaje, que tiene que reconstruir toda la función.
fuxia
1
Sí, tendrías que escribir todo el método, sin duda, pero para las personas que necesitan (digamos ...) agregarlo, podría ahorrarles muchos dolores de cabeza. Y todo esto es culpa de WP. Arrrgh!
nevvermind
Agradable y lo he usado en esta respuesta modificando un poco, tal vez puedas mejorarlo si me perdí algo, gracias.
The Alpha
Lo que realmente necesitaba era el wp_nav_menu , pero necesitaba cambiar el parámetro 'container_class', para trabajar para mi caso de uso particular, donde en alguna condición cambié el menú principal por otro, pero necesitaba que las clases fueran consistentes para css.
D. Dan
33

¡Desde WordPress 3.0 , ya no necesita un andador personalizado!

Existe el walker_nav_menu_start_elfiltro, consulte 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 ) {
        // append description after link
        $item_output .= sprintf('<span class="description">%s</span>', esc_html($item->description));

        // insert description as last item *in* link ($input_output ends with "</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);
Joost
fuente
1
¡Agradable! Estaba usando la solución nav walker de @toscho, pero esto es mucho más limpio y fácil de mantener. Esta debería ser la respuesta aceptada, una práctica mucho mejor.
Neejoh
8

Esto no es mejor ni peor que otras sugerencias; Es simplemente diferente. Es corto y dulce también.

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

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

También sería fácil usar jQuery para agregarlo, pero el texto es lo suficientemente ornamental como para que CSS parezca apropiado.

mrwweb
fuente
2

También puede escribir un <span>elemento después de la etiqueta de navegación en los menús y usar la siguiente regla CSS para cambiar su displayconfiguración (es inlinepor defecto):

span {display:block}
Markus
fuente
2
Bueno, es una solución simple y fácil, pero ¿por qué usarlo spansi lo haces bloquear de todos modos? xhtml / html4 no permite elementos de bloque dentro de los enlaces, html5 sí lo hace, así que solo use div, ¡y no necesita ningún CSS!
James Mitch