Selector rápido del año para el calendario jQuery

  • Por
Cómo hacer un control para poder seleccionar el año que queremos ver en el calendario jQuery.
Si hemos seguido el Manual del Calendario jQuery habremos comprobado que tenemos un sistema para la selección de fechas bastante funcional. La única cosilla que nos facilitaría bastante la selección de fechas es poder seleccionar directamente un año, sin tener que navegar por los meses siguiente y anterior.

Imaginar que necesitamos seleccionar una fecha que está 6 años hacia delante en el calendario. Tendríamos que ir 12 x 6 meses hacia delante para poder mostrar el mes y año del que queremos seleccionar la fecha y eso es poco práctico.

Así que, para evitar que la navegación por meses se quede corta, vamos a hacer un pequeño control para poder seleccionar cualquier año. El resultado de lo que vamos a realizar se puede ver en una página aparte.

Control para el cambio de año

En este ejemplo lo único que vamos a crear es un botón para el cambio de año. Ese botón aparecerá al lado del año en el calendario y al pulsarlo creará una capa con diversos números de año (10 años para atrás y otros 10 para adelante). Las diversas opciones de año se podrán pulsar y entonces desaparecerá la capa de los años y se actualizará el año del calendario.

var botonCambioAno = $('<a href="#" class="botoncambiaano"><span></span></a>')
botonCambioAno.click(function(e){
   e.preventDefault();
   var botonActivoSelAnos = $(this);
   //creo una capa con una serie de años para elegir
   var capaAnos = $('<div class="capaselanos"></div>');
   //genero 10 años antes y 10 después
   for (var i=ano-10; i<=ano+10; i++){
      var codigoEnlace = '<a href="#"';
      if (i==ano)
         codigoEnlace += ' class="seleccionado"';
      if (i==ano+10)
         codigoEnlace += ' class="ultimo"';
      codigoEnlace += '><span>' + i + '</span></a>';
      var opcionAno = $(codigoEnlace);
      opcionAno.click(function(e){
         e.preventDefault();
         ano = parseInt($(this).children().text());
         capaDiasMes.empty();
         muestraDiasMes(mes, ano);
         capaAnos.slideUp();
         capaAnos.detach();
      })
      capaAnos.append(opcionAno);
   }
   //coloco la capa en la página
   $(document.body).append(capaAnos);
   //posiciono la capa
   capaAnos.css({
      top: (botonActivoSelAnos.offset().top + 12) + "px",
      left: (botonActivoSelAnos.offset().left - 25) + "px"
   })
   capaAnos.slideDown();
})

Eso es todo, con esta modificación ya queda listo el calendario jQuery, por lo menos todo lo que hemos querido implementar en estas fases. Seguramente otras personas necesitarán otras modificaciones, y esperamos que al haber visto el calendario en sus distintas etapas sea más sencillo encontrar por uno mismo el lugar o lugares donde se debe modificar.

Con todo, hemos creado un calendario dinámico para la selección de fechas en menos de 300 líneas de código, que ocupa 9 KB de código Javascript, lo que lo hace más que razonablemente ligero para utilizar en cualquier proyecto. Además, a través de la hoja de estilos, cualquier persona podrá alterar radicalmente el aspecto del calendario para adaptarlo al look & feel de su sitio web.

Código completo del plugin calendario

Para acabar, vamos a colocar el código Javascript completo del plugin Calendario jQuery.

jQuery.fn.calendarioDW = function() {
this.each(function(){
      //saber si estoy mostrando el calendario
      var mostrando = false;
      //variable con el calendario
      var calendario;
      //variable con los días del mes
      var capaDiasMes;
      //variable para mostrar el mes y ano que se está viendo
      var capaTextoMesAnoActual = $('<div class="visualmesano"></div>');
      //iniciales de los días de la semana
      var dias = ["l", "m", "x", "j", "v", "s", "d"];
      //nombres de los meses
      var nombresMes = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]
      
      //elemento input
      var elem = $(this);
      //creo un enlace-botón para activar el calendario
      var boton = $("<a class='botoncal' href='#'><span></span></a>");
      //inserto el enlace-botón después del campo input
      elem.after(boton);
      
      //evento para clic en el botón
      boton.click(function(e){
         e.preventDefault();
         mostrarCalendario();
      });
      //evento para clic en el campo
      elem.click(function(e){
         this.blur();
         mostrarCalendario();
      });
      
      //función para mostrar el calendario
      function mostrarCalendario(){
         if(!mostrando){
            mostrando = true;
            //es que hay que mostrar el calendario
            //dias de la semana
            var capaDiasSemana = $('<div class="diassemana"></div>');
            $(dias).each(function(indice, valor){
               var codigoInsertar = '<span';
               if (indice==0){
                  codigoInsertar += ' class="primero"';
               }
               if (indice==6){
                  codigoInsertar += ' class="domingo ultimo"';
               }
               codigoInsertar += ">" + valor + '</span>';
               
               capaDiasSemana.append(codigoInsertar);
            });
            
            //capa con los días del mes
            capaDiasMes = $('<div class="diasmes"></div>');
            
            //un objeto de la clase date para calculo de fechas
            var objFecha = new Date();
            //miro si en el campo INPUT tengo alguna fecha escrita
            var textoFechaEscrita = elem.val();
            if (textoFechaEscrita!= ""){
               if (validarFechaEscrita(textoFechaEscrita)){
                  var arrayFechaEscrita = textoFechaEscrita.split("/");
                  //hago comprobación sobre si el año tiene dos cifras
                  if(arrayFechaEscrita[2].length == 2){
                     if (arrayFechaEscrita[2].charAt(0)=="0"){
                        arrayFechaEscrita[2] = arrayFechaEscrita[2].substring(1);
                     }
                     arrayFechaEscrita[2] = parseInt(arrayFechaEscrita[2]);
                     if (arrayFechaEscrita[2] < 50)
                        arrayFechaEscrita[2] += 2000;
                  }
                  objFecha = new Date(arrayFechaEscrita[2], arrayFechaEscrita[1]-1, arrayFechaEscrita[0])
               }
            }
            
            //mes y año actuales
            var mes = objFecha.getMonth();
            var ano = objFecha.getFullYear();
            //muestro los días del mes y año dados
            muestraDiasMes(mes, ano);
            
            //control para ocultar el calendario
            var botonCerrar = $('<a href="#" class="calencerrar"><span></span></a>');
            botonCerrar.click(function(e){
               e.preventDefault();
               calendario.hide("slow");
            })
            var capaCerrar = $('<div class="capacerrar"></div>');
            capaCerrar.append(botonCerrar)
            
            //controles para ir al mes siguiente / anterior
            var botonMesSiguiente = $('<a href="#" class="botonmessiguiente"><span></span></a>');
            botonMesSiguiente.click(function(e){
               e.preventDefault();
               mes = (mes + 1) % 12;
               if (mes==0)
                  ano++;
               capaDiasMes.empty();
               muestraDiasMes(mes, ano);
            })
            var botonMesAnterior = $('<a href="#" class="botonmesanterior"><span></span></a>');
            botonMesAnterior.click(function(e){
               e.preventDefault();
               mes = (mes - 1);
               if (mes==-1){
                  ano--;
                  mes = 11
               }   
               capaDiasMes.empty();
               muestraDiasMes(mes, ano);
            })
            var botonCambioAno = $('<a href="#" class="botoncambiaano"><span></span></a>')
            botonCambioAno.click(function(e){
               e.preventDefault();
               var botonActivoSelAnos = $(this);
               //creo una capa con una serie de años para elegir
               var capaAnos = $('<div class="capaselanos"></div>');
               //genero 10 años antes y 10 después
               for (var i=ano-10; i<=ano+10; i++){
                  var codigoEnlace = '<a href="#"';
                  if (i==ano)
                     codigoEnlace += ' class="seleccionado"';
                  if (i==ano+10)
                     codigoEnlace += ' class="ultimo"';
                  codigoEnlace += '><span>' + i + '</span></a>';
                  var opcionAno = $(codigoEnlace);
                  opcionAno.click(function(e){
                     e.preventDefault();
                     ano = parseInt($(this).children().text());
                     capaDiasMes.empty();
                     muestraDiasMes(mes, ano);
                     capaAnos.slideUp();
                     capaAnos.detach();
                  })
                  capaAnos.append(opcionAno);
               }
               //coloco la capa en la página
               $(document.body).append(capaAnos);
               //posiciono la capa
               capaAnos.css({
                  top: (botonActivoSelAnos.offset().top + 12) + "px",
                  left: (botonActivoSelAnos.offset().left - 25) + "px"
               })
               capaAnos.slideDown();
            })
            
            //capa para mostrar el texto del mes y ano actual
            var capaTextoMesAno = $('<div class="textomesano"></div>');
            var capaTextoMesAnoControl = $('<div class="mesyano"></div>')
            capaTextoMesAno.append(botonMesSiguiente);
            capaTextoMesAno.append(botonMesAnterior);
            capaTextoMesAno.append(capaTextoMesAnoControl);
            capaTextoMesAnoControl.append(capaTextoMesAnoActual);
            capaTextoMesAnoControl.append(botonCambioAno);
            
            //calendario y el borde
            calendario = $('<div class="capacalendario"></div>');
            var calendarioBorde = $('<div class="capacalendarioborde"></div>');
            calendario.append(calendarioBorde);
            calendarioBorde.append(capaCerrar);
            calendarioBorde.append(capaTextoMesAno);
            calendarioBorde.append(capaDiasSemana);
            calendarioBorde.append(capaDiasMes);
            
            //inserto el calendario en el documento
            $(document.body).append(calendario);
            //lo posiciono con respecto al boton
            calendario.css({
               top: boton.offset().top + "px",
               left: (boton.offset().left + 20) + "px"
            })
            //muestro el calendario
            calendario.show("slow");
            
         }else{
            //es que el calendario ya se estaba mostrando...
            calendario.fadeOut("fast");
            calendario.fadeIn("fast");
            
         }
      }
      
      function muestraDiasMes(mes, ano){
         //console.log("muestro (mes, ano): ", mes, " ", ano)
         //muestro en la capaTextoMesAno el mes y ano que voy a dibujar
         capaTextoMesAnoActual.text(nombresMes[mes] + " " + ano);
         
         //muestro los días del mes
         var contadorDias = 1;
         
         //calculo la fecha del primer día de este mes
         var primerDia = calculaNumeroDiaSemana(1, mes, ano);
         //calculo el último día del mes
         var ultimoDiaMes = ultimoDia(mes,ano);
         
         //escribo la primera fila de la semana
         for (var i=0; i<7; i++){
            if (i < primerDia){
               //si el dia de la semana i es menor que el numero del primer dia de la semana no pongo nada en la celda
               var codigoDia = '<span class="diainvalido';
               if (i == 0)
                  codigoDia += " primero";
               codigoDia += '"></span>';
            } else {
               var codigoDia = '<span';
               if (i == 0)
                  codigoDia += ' class="primero"';
               if (i == 6)
                  codigoDia += ' class="ultimo domingo"';
               codigoDia += '>' + contadorDias + '</span>';
               contadorDias++;
            }
            var diaActual = $(codigoDia);
            capaDiasMes.append(diaActual);
         }
         
         //recorro todos los demás días hasta el final del mes
         var diaActualSemana = 1;
         while (contadorDias <= ultimoDiaMes){
            var codigoDia = '<span';
            //si estamos a principio de la semana escribo la clase primero
            if (diaActualSemana % 7 == 1)
               codigoDia += ' class="primero"';
            //si estamos al final de la semana es domingo y ultimo dia
            if (diaActualSemana % 7 == 0)
               codigoDia += ' class="domingo ultimo"';
            codigoDia += '>' + contadorDias + '</span>';
            contadorDias++;
            diaActualSemana++;
            var diaActual = $(codigoDia);
            capaDiasMes.append(diaActual);
         }
         
         //compruebo que celdas me faltan por escribir vacias de la última semana del mes
         diaActualSemana--;
         if (diaActualSemana%7!=0){
            //console.log("dia actual semana ", diaActualSemana, " -- %7=", diaActualSemana%7)
            for (var i=(diaActualSemana%7)+1; i<=7; i++){
               var codigoDia = '<span class="diainvalido';
               if (i==7)
                  codigoDia += ' ultimo'
               codigoDia += '"></span>';
               var diaActual = $(codigoDia);
               capaDiasMes.append(diaActual);
            }
         }
         
         //crear el evento para cuando se pulsa un día de mes
         //console.log(capaDiasMes.children());
         capaDiasMes.children().click(function(e){
            var numDiaPulsado = $(this).text();
            if (numDiaPulsado != ""){
               elem.val(numDiaPulsado + "/" + (mes+1) + "/" + ano);
               calendario.fadeOut();
            }
         })
      }
      //función para calcular el número de un día de la semana
      function calculaNumeroDiaSemana(dia,mes,ano){
         var objFecha = new Date(ano, mes, dia);
         var numDia = objFecha.getDay();
         if (numDia == 0)
            numDia = 6;
         else
            numDia--;
         return numDia;
      }
      
      //función para ver si una fecha es correcta
      function checkdate ( m, d, y ) {
         // función por http://kevin.vanzonneveld.net
         // extraida de las librerías phpjs.org manual en http://www.desarrolloweb.com/manuales/manual-librerias-phpjs.html
         return m > 0 && m < 13 && y > 0 && y < 32768 && d > 0 && d <= (new Date(y, m, 0)).getDate();
      }
      
      //funcion que devuelve el último día de un mes y año dados
      function ultimoDia(mes,ano){
         var ultimo_dia=28;
         while (checkdate(mes+1,ultimo_dia + 1,ano)){
          ultimo_dia++;
         }
         return ultimo_dia;
      }
      
      function validarFechaEscrita(fecha){
         var arrayFecha = fecha.split("/");
         if (arrayFecha.length!=3)
            return false;
         return checkdate(arrayFecha[1], arrayFecha[0], arrayFecha[2]);
      }
});
return this;
};

Si lo deseas, puedes ver este último ejemplo de calendario en una página aparte.

Recuerda que si deseas aprender a usar este calendario en tu proyecto debes seguir las explicaciones del artículo Pasos para implementar el plugin calendario jQuery.

Por nuestra parte y por el momento, nada más... esperamos que este desarrollo haya sido fácil de entender y sea fácil de utilizar por los interesados en utilizar un datepicker en sus proyectos. Sobre todo, esperamos que una persona con conocimientos de jQuery pueda modificarlo para adaptarlo a las necesidades propias de cada uno.

Finalizamos solicitando a los lectores que si modifican el calendario nos manden una copia para publicarlo en este manual. Gracias!

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Joseba

01/3/2011
Error al cerrar el calendario
SI despliegas el año y cierras el calendario con la x, la lista de años no de oculta.
:)

wArLeY996

01/3/2011
Un bug en el calendario...
Esta muy bonito el calendario y tiene buenos efectos.
Mi comentario es para mejorarlo.
Cuando abres div-combo de años y no seleccionas, en vez de eso se da clik en cerrar, el div-combo sigue alli, no se cierra.

DarkF

05/5/2011
CALENDARIO DE EVENTOS
Hola!

Hay alguna forma de modificar este calendario para que se visualice un enlace en los dias que posean eventos??

MARCO ANTONIO

26/1/2016
Duda
Si se agregan los inputs dinamicamente se deja de visualizar el calendario, como se podría solucionar esto