CésarHdz.com

Por default, WordPress agrega a los menús de navegación atributos que no siempre necesitamos ya sea porque son muy específicos o porque nuestro menú no es tan complejo. Por otro lado, a pesar de tantos atributos existen algunos que nos serían muy útiles pero que no los incluye el menú, como por ejemplo la posibilidad de agregar una clase a los elementos que tengan descendientes. Para adaptar el menú a nuestras necesidades, podemos aplicar filtros o utilizar un Walker. De las dos opciones, la segunda es la que tiene posibilidades de personalización.

Estructura básico del Menú con HTML5

Antes de comenzar quiero dar un breve repaso a la estructura básica para crear menús:

<nav>
    <ul>
        <li><a href="#">Enlace</a></li>
        <li><a href="#">Enlace</a></li>
        <li><a href="#">Enlace</a>
            <ul>
                <li><a href="#">Subenlace</a></li>
            </ul>
        </li>
    </ul>
</nav>

En esta estructura podemos observar ciertos patrones, por ejemplo los elementos siempre estarán repesentados por una etiqueta li, por esta razón las clases menu-item no tiene mucho sentido, otro ejemplo son los submenús pues para acceder a ellos podemos escribir ul ul.

Creación del WalKer

El Walker es una extensión de la clase Walker_Nav_Menu, así que crearemos un archivo dentro de un folder que podría ser functions que se llame mi_menu_walker.php y escribiremos el siguente código.

class Mi_menu_walker extends Walker_Nav_Menu
{
    public function start_el( &$output, $item, $depth, $args )
    {
        /*
         * Abrimos el elemento, por el momento si clases
         */ 

        $output .= '<li>';

        /*
         * Atributos del enlace
         */ 
        // Evitamos titulos redundantes
        ! empty ( $item->attr_title )
            and $item->attr_title !== $item->title
            and $attributes .= ' title="' . esc_attr( $item->attr_title ) .'"';

        ! empty ( $item->url )
            and $attributes .= ' href="' . esc_attr( $item->url ) .'"';

        //Mostramos el atributo rel
        ! empty( $item->xfn )  
            and $attributes .= ' rel="'    . esc_attr( $item->xfn        ) .'"';

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

        $anchor = "{$args->before}<a {$attributes}>{$args->link_before}{$title}</a>"
                    . $args->link_after . $args->after;

        //Agregamos un filtro par si un plugin necesita acceso
        $output .= apply_filters(
            'walker_nav_menu_start_el'
            ,   $anchor
            ,   $item
            ,   $depth
            ,   $args
        );
    }

    function end_el( &$output )
    {
        $output .= '</li>';
    }
}

Con esto tenemos una estructura limpia a la que podemos agregar los atributos que necesitamos. Dentro de la función start_el tenemos acceso a diferentes variables, la primera $output debe incluirse como referencia1 porque se trata de el menú, así que debe incluirse el operador .= para agreagr información. La variable $item es un objeto que contiene la información del enlace, de forma similar a un post2, es de este objeto de donde tomaremos los datos para trabajar.

Lo que falta es incluir este archivo al tema: dentro de nuestro archivo functions.php lo incluimos con include functions/mi_menu_walker.php

Asignar el Walker a un Menú

Para ver los resultados necesitamos decirle a WordPress en que menú queremos que utilice nuestro Walker, lo hacemos en nuestro tema al ejecutar el menu con el siguiente código:

$args = array(
    [...]
    'walker' => new Mi_menu_walker;
);

wp_nav_menu($args);

Personalizar el Menú

Una vez implementado el Walker, vamos a ir agregando los atributos que considero serían los más importantes. Trabajaremos exclusivamente dentro de la funcion start_el.

Agregar Clase de elemento actual

La clase actual o en inglés current nos indica en que elemento nos encontramos, para agregarla debemos buscar en la variable $item. Considero que no es necesaria tanta especificidad en las clases, y con utilizar la clase current independientemente del nivel, podemos acceder a todos ellos. Y como utilizaremos varias clases comenzaremos guardando estas dentro de un array.

$classes = array();
$classes[] = ($item->current OR $item->current_item_ancestor OR $item->current_item_parent)
        ? 'current' : '';

Clase para elemento con descendientes

Para agregar esta clase, Jan Fabry propone que primero tenemos que forzar a que WordPress incluya la variable has_children dentro del array $args, para ello agregamos el siguiente método a nuestro Walker

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

Y ahora dentro de nuestra función podemos agregar la clase parent, según el valor de la variable has_children.

$classes[] = ($arg->has_children) ? 'parent' : '';

Clase para identificar elementos

Poder identificar elementos individualmente, nos permite agregar estilos diferentes a cada uno ellos. Por default WordPress agrega un id de la siguiente forma a los elementos: id="item-35", siendo 35 el identificador en la base de datos, el cual varia en cada instalación, por lo que podemos tener un id cuando trabajamos de manera local y otro cuando publicamos el sitio o tema. Una solución podría ser que en lugar de agregar el id como identificador utilicemos el nombre o slug como una clases así tendríamos, por ejemplo, class="contacto". Para incluir dicha clase necesitamos el siguiente código.

$classes[] = sanitize_html_class(sanitize_title($item->title));

Agregar las clases al elemento li

En nuestro array $classes ya tenemos guardadas las clases que necesitamos, ahora las agregamos al elemento con el siguiente código.

$output     .= '<li class="' . trim(implode(' ', $classes)). '">';

Agregar Descripción

Aunque la descripción no es muy utilizada, creo que podrían crearse menús como el de [css-trick], donde además del enlace tenemos una breve descripción de lo que trata. Para agregar la descripción necesitamos el siguiente código.

 $description = ($item->description != '') 
                ? "<span class=\"description\"><small>{$item->description}</small></span>"
                : '';

No se si las etiquetas span y small son las más adecuadas, pero utilizo la etiqueta small no para hacer el texto más pequeño, sino para establecer jerarquías3.

Clase Mi_menu_walker

Puedes descargar la clase completa desde GitHub.

Referencias


  1. Creo que en lugar de utilizar la variable output de esta manera, se debió haber utilizado una propiedad, por ejemplo $this->menu_output

  2. De hecho los enlaces son tratados como entradas en la base de datos de WordPress 

  3. Ver http://html5doctor.com/small-hr-element/ 

Posted in Desarrollo