Ya que en el Manual de jQuery hemos aprendido a realizar diversos efectos en jQuery y a lidiar con colas de efectos, en este artículo vamos a mostrar cómo hacer unas mejoras sencillas en el plugin para utilizar efectos especiales.
Realmente veremos que lo que hemos estudiado para aprender a hacer efectos es incluso bastante más de lo que necesitaremos en ejemplos medianamente avanzados como el que tenemos entre manos.
Antes de empezar con las explicaciones de los cambios realizados, dejamos un enlace al ejemplo en funcionamiento.
submenu.css("display", "block");
Por un efecto que abre el submenú con una cortinilla hacia abajo, con:
submenu.slideDown("slow");
retardo = setTimeout("submenu.css('display', 'none');",1000)
Por:
retardo = setTimeout("submenu.slideUp('slow');",1000)
Además, en el evento mouseout de las capas de los submenús, también hacemos este cambio de sentencias, también para que se oculten al salír de los submenús con un retardo y con un efecto.
function ocultarTodosSubmenus(noOcultar){
$.each(arrayCapasSubmenu, function(ind){
if (ind!=noOcultar){
this.stop(true, true);
this.css("display", "none");
}
});
}
Este método oculta todas las capas de submenús y se invoca cuando mostramos un menú desde un enlace principal, para asegurarnos que no haya dos menús abiertos al mismo tiempo. Para empezar, ahora recibe un índice de submenú que no deseamos ocultar (porque deba permanecer abierto).
Pero lo imporante es que hemos colocado un método stop(true, true), que sirve para hacer que se interrumpan todos los efectos de mostrar o ocultar una capa que pudieran haber en funcionamiento. Además marcamos con los dos true que la cola de efectos se limpie y que los efectos que se estaban ejecutando pasen directamente a su estado final.
////////////////////////////////////////////////////////////////////////////
//creación del plugin generaMenu.
//envío el menú de opciones como parámetro
////////////////////////////////////////////////////////////////////////////
(function($) {
$.fn.generaMenu = function(menu) {
this.each(function(){
var retardo;
var capaMenu = $(this);
//creo e inserto la lista principal
var listaPrincipal = $('<ul></ul>');
capaMenu.append(listaPrincipal);
//enlaces principales
var arrayEnlaces = [];
var arrayCapasSubmenu = [];
var arrayLiMenuPrincipal = [];
//recorro los elementos del menú
jQuery.each(menu, function(ind) {
//ahora en this tengo cada uno de los elementos.
var elementoPrincipal = $('<li></li>');
listaPrincipal.append(elementoPrincipal);
//creo el enlace e inserto
var enlacePrincipal = $('<a href="' + this.url + '">' + this.texto + '</a>');
elementoPrincipal.append(enlacePrincipal);
var capaSubmenu = $('<div class="submenu"></div>');
//guardo la capa submenu en el elemento enlaceprincipal y su indice
enlacePrincipal.data("capaSubmenu",capaSubmenu);
enlacePrincipal.data("indice",ind);
//creo una lista para poner los enlaces
var subLista = $('<ul></ul>');
//añado la lista a capaMenu
capaSubmenu.append(subLista);
//para cada elace asociado
jQuery.each(this.enlaces, function() {
//en this tengo cada uno de los enlaces
//creo el elemento de la lista del submenú actual
var subElemento = $('<li></li>');
//meto el elemento de la lista en la lista
subLista.append(subElemento);
//creo el enlace
var subEnlace = $('<a href="' + this.url + '">' + this.texto + '</a>');
//cargo el enlace en la lista
subElemento.append(subEnlace);
});
//inserto la capa del submenu en el cuerpo de la página
$(document.body).append(capaSubmenu);
/////////////////////////////////////////
//EVENTOS
/////////////////////////////////////////
//defino el evento mouseover para el enlace principal
enlacePrincipal.mouseover(function(e){
var enlace = $(this);
clearTimeout(retardo);
indice = enlace.data("indice");
ocultarTodosSubmenus(indice);
//recupero la capa de submenu asociada
submenu = enlace.data("capaSubmenu");
//la muestro
//submenu.css("display", "block");
submenu.slideDown("slow");
});
//defino el evento para el enlace principal
enlacePrincipal.mouseout(function(e){
var enlace = $(this);
//recupero la capa de submenu asociada
submenu = enlace.data("capaSubmenu");
//la oculto
clearTimeout(retardo);
//retardo = setTimeout("submenu.css('display', 'none');",1000)
retardo = setTimeout("submenu.slideUp('slow');",1000)
});
//evento para las capa del submenu
capaSubmenu.mouseover(function(){
clearTimeout(retardo);
})
//evento para las capa del submenu
capaSubmenu.mouseout(function(){
clearTimeout(retardo);
submenu = $(this);
retardo = setTimeout("submenu.slideUp('slow');",1000)
})
//evento para cuando se redimensione la ventana
if(arrayEnlaces.length==0){
//Este evento sólo lo quiero ejecutar una vez
$(window).resize(function(){
colocarCapasSubmenus();
});
}
/////////////////////////////////////////
//FUNCIONES PRIVADAS DEL PLUGIN
/////////////////////////////////////////
//una función privada para ocultar todos los submenus
function ocultarTodosSubmenus(noOcultar){
$.each(arrayCapasSubmenu, function(ind){
if (ind!=noOcultar){
this.stop(true, true);
this.css("display", "none");
}
});
}
function colocarCapasSubmenus(){
$.each(arrayCapasSubmenu, function(i){
//coloco la capa en el lugar donde me interesa
var posicionEnlace = arrayLiMenuPrincipal[i].offset();
this.css({
left: posicionEnlace.left,
top: posicionEnlace.top + 28
});
});
}
//guardo el enlace y las capas de submenús y los elementos li en arrays
arrayEnlaces.push(enlacePrincipal);
arrayCapasSubmenu.push(capaSubmenu);
arrayLiMenuPrincipal.push(elementoPrincipal);
//coloco inicialmente las capas de submenús
colocarCapasSubmenus();
});
});
return this;
};
})(jQuery);
El resultado, como decimos, es bastante más atractivo y los efectos no resultan incómodos en la operativa del menú, como podemos ver en la página donde mostramos el menú desplegable en marcha.