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
- http://wordpress.stackexchange.com/questions/37325/wp-nav-menu-how-to-change-li-class
- http://wordpress.stackexchange.com/questions/14037/menu-items-description-custom-walker-for-wp-nav-menu/14039#14039
- https://gist.github.com/1053467