> Manuales > Taller de jQuery

Implementamos efectos en el navegador desplegable jQuery, aplicando conocimientos de colas de efectos, para hacer un plugin de navegación más vistoso.

Uno de los ejemplos más típicos que podemos imaginar con jQuery es la realización de un plugin que implemente un navegador dinámico desplegable. En un pasado artículo ya presentamos la manera de implementar esta utilidad de navegación que agregaría bastante dinamismo y vistosidad a nuestras páginas.

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.

Cambio método css("display", "block") por efecto slideDown()

En el evento mouseover definido para los enlaces principales hemos cambiado el método:

submenu.css("display", "block");

Por un efecto que abre el submenú con una cortinilla hacia abajo, con:

submenu.slideDown("slow");

Cambio método submenu.css('display', 'none') por efecto con slideUp()

De manera similar, cambiamos el evento mouseout para los enlaces principales, para que se oculten con una cortinilla hacia arriba. Esto se hizo cambiando las instrucciones:

retardo = setTimeout("submenu.css('display', 'none');",1000)

Por:

retardo = setTimeout("submenu.slideUp('slow');",1000)

Nota: los setTimeout() en este caso permanecen y no los cambiamos por delay() por mayor comodidad y porque no funcionaría (dado que en ocasiones ese retardo se anulará con clearTimeout() y delay() no lo permite).

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.

Cambios en la función que oculta todos los submenús

Esta función es interesante, porque tiene un par de cambios importantes.

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.

Código completo del plugin navegador desplegable con efectos

Para que se pueda ver todo de una manera más global, dejamos el código completo de este plugin, una vez realizadas todas las modificaciones para aplicar los efectos.

////////////////////////////////////////////////////////////////////////////
//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.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual