Agregar la clase 'has_children' al elemento li padre al modificar Walker_Nav_Menu

10 may 2011, 10:46:04
Vistas: 38.8K
Votos: 24

Estoy escribiendo una clase walker personalizada para wp_nav_menu y quiero poder especificar si un elemento li contiene un submenú. Quiero que mi marcado sea así:

<li class="has_children [otras-clases-wordpress]">
    <a class="parent-link">Algún elemento</a>
    <ul class="sub-menu">

Sé cómo agregar y eliminar las clases correctamente, solo que no puedo encontrar nada que me indique si el elemento actual tiene elementos hijos.

¿Alguna idea?

Gracias de antemano.

0
Todas las respuestas a la pregunta 4
3
27

El método start_el() debería recibir esta información en su parámetro $args, pero parece que WordPress solo llena esto si $args es un array, mientras que para los menús de navegación personalizados es un objeto. Esto está reportado en un ticket de Trac. Pero no hay problema, puedes llenar esto tú mismo, si también sobrescribes el método display_element() en tu walker personalizado (porque este es el lugar más fácil para acceder al array de elementos hijos):

class WPSE16818_Walker extends Walker_Nav_Menu
{
    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output )
    {
        $id_field = $this->db_fields['id'];
        if ( is_object( $args[0] ) ) {
            $args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
        }
        return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }

    function start_el( &$output, $item, $depth, $args ) {
        if ( $args->has_children ) {
            // ...
        }
    }
10 may 2011 11:45:58
Comentarios

Hola Jan, ¿Puedes ayudarme con esta pregunta? Intenté tu código pero no pude hacerlo funcionar. ¿Podrías darme un poco más de código de ejemplo?

PrivateUser PrivateUser
4 feb 2012 00:18:13

Consulta el ejemplo de implementación completo más abajo en esta página.

rjb rjb
6 mar 2013 22:41:15

Muchas gracias @Jan fabry.. Estaba atascado con mi walker personalizado.. Al final tu fragmento de código me ayudó.

Harish Kanakarajan Harish Kanakarajan
5 may 2014 13:12:39
2

Actualización: A partir de WordPress 3.7 (octubre de 2013), se han añadido clases CSS para indicar elementos y páginas hijas en los menús de temas — no es necesario usar un walker personalizado ya que está incluido en el núcleo de WordPress.

Las clases CSS se llaman menu-item-has-children y page_item_has_children.


Para una solución completa para cualquiera que tenga prisa (crédito a la respuesta previa de Jan Fabry), mira la implementación completa a continuación.

Genera la navegación en la plantilla de tu tema:

wp_nav_menu( array(
    'theme_location' => 'navigation-primary',
    'container' => false,
    'container_class' => '',
    'container_id' => '',
    'menu_class' => '',
    'menu_id' => '',
    'walker' => new Selective_Walker(),
    'depth' => 2
    )
);

Luego, incluye lo siguiente en el archivo functions.php de tu tema:

class Selective_Walker extends Walker_Nav_Menu {
    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
        $id_field = $this->db_fields['id'];

        if ( is_object( $args[0] ) ) {
            $args[0]->has_children = !empty( $children_elements[$element->$id_field] );
        }

        return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }

    function start_el( &$output, $item, $depth, $args ) {
        if ( $args->has_children ) {
            $item->classes[] = 'has_children';
        }

        parent::start_el(&$output, $item, $depth, $args);
    }
}

El resultado en HTML será similar al siguiente:

<ul>
    <li><a href="#">Inicio</a></li>
    <li class="has_children"><a href="#">Nosotros</a>
        <ul class="sub-menu">
            <li><a href="#">Nuestra Misión</a></li>
        </ul>
    </li>
    <li><a href="#">Servicios</a></li>
    <li class="has_children"><a href="#">Productos</a>
        <ul class="sub-menu">
            <li><a href="#">Lorem Ipsum</a></li>
            <li><a href="#">Lorem Ipsum</a></li>                
        </ul>
    </li>
    <li><a href="#">Contáctanos</a></li>
</ul>

Para más información sobre cómo usar la clase walker de WordPress, consulta Understanding the Walker Class.

¡Disfrútalo!

6 mar 2013 21:30:09
Comentarios

Error fatal: Se ha eliminado el paso por referencia en tiempo de llamada en D:\www\wordpress\wp-content\themes\wpt_theme\functions.php en la línea 44

Tahir Yasin Tahir Yasin
14 jul 2014 22:43:55

La línea 44 es: parent::start_el(&$output, $item, $depth, $args);

Tahir Yasin Tahir Yasin
14 jul 2014 22:44:23
1

Esta función es exactamente lo que necesitas tener. También te muestra una forma bastante efectiva de modificar los elementos del menú de navegación. Además, puedes abrirla para funciones más avanzadas (por ejemplo, en un tema hijo) mediante el filtro de elementos:

/**
 * Clases para una navegación llamada "Topnav" en la ubicación del menú "top".
 * Muestra ejemplos sobre cómo modificar el elemento actual del menú de navegación
 * 
 * @param (object) $items
 * @param (object) $menu
 * @param (object) $args
 */
function wpse16818_nav_menu_items( $items, $menu, $args )
{
    # >>>> comienza la edición

    // ejemplos de posibles objetivos
    $target['name'] = 'Topnav';
    // Los elementos del menú objetivo/s
    $target['items'] = array( (int) 6 );

    # <<<< detén la edición

    // filtro para temas hijos: "config_nav_menu_topnav"
    $target = apply_filters( 'config_nav_menu_'.strtolower( $target['name'] ), $target );

    // Abortar si no estamos con el menú nombrado
    if ( $menu->name !== $target['name'] ) 
        return;

    foreach ( $items as $item )
    {
        // Comprueba qué contiene $item
        echo '<pre>'; print_r($item); echo '</pre>';

        // Primer ejemplo del mundo real:
        $item->classes = 'span-4';

        // Segundo ejemplo del mundo real:
        // Añade esta clase si estamos en uno de los elementos objetivo
        if ( in_array( (int) $item->menu_order, $target['items'] ) )
            $item->classes .= ' last';
    }

    return $items;
}
add_filter( 'wp_get_nav_menu_items', 'wpse16818_nav_menu_items', 10, 3 );

Y sí, en casi todos los casos no hay necesidad de un walker personalizado.

10 may 2011 22:36:49
Comentarios

¡Gracias! Por ahora necesito el walker, pero echaré un vistazo a esto para la próxima vez.

patnz patnz
11 may 2011 02:50:17
1

Si deseas crear un menú desplegable, puedes hacerlo solo con CSS. Crea un menú personalizado en WordPress con elementos hijos, WordPress automáticamente asigna la clase .sub-menu al ul hijo. Prueba este CSS:

    nav li {position:relative;}
   .sub-menu {display:none; position:absolute; width:300px;}
    nav ul li:hover ul {display:block;}

Puedes añadir algo de jQuery para mejorarlo un poco, pero esto debería darte un menú desplegable funcional.

10 may 2011 11:07:48
Comentarios

¡Gracias! Es para un menú de árbol colapsable de varios niveles en el que también estoy insertando elementos de control, pero definitivamente es bueno hacer tanto como sea posible con CSS.

patnz patnz
11 may 2011 02:49:02