Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. JavaScript

Cómo crear un hermoso calendario

by
Read Time:19 minsLanguages:

Spanish (Español) translation by Jorge Ortega (you can also view the original English article)

En el tutorial y screencast especial de hoy, voy a enseñarte cómo construir un elegante módulo de calendario. Vamos a usar CSS3 para darle una aparencia brillante, y luego añadirle un poco de funcionalidad bien ordenada con JavaScript.


Paso 0. La idea

Yo sigo el blog 365PSD, un sitio realmente atractivo que ofrece un PSD gratis - usualmente alguna pieza de UI - cada día. Para el día 81, publicaron un módulo de calendario realmente atractivo. Me supuse que no iba a ser muy difícil construir el módulo web, así que te voy a enseñar cómo hacer eso hoy!



Paso 1. El HTML

Vamos a empezar construyendo nuestra estructura HTML. Por supuesto, vamos a empezar con el esqueleto.

Así que, dentro de body, vamos a empezar con un div para contener todo; luego, vamos a tener tres secciones principales dentro de eso:

Primero, tenemos el div.header; regresando a nuestro PSD, vemos que corresponde a la parte superior, la sección que contiene el mes, los botones para mover los meses, y los "ganchos". Luego tenemos una tabla para los nombres de los días. Finalmente, tenemos un div#cal-frame. Este es el grid del calendario.

Voy a decirte un secreto. Cuando construí originalmente este UI para el calendario, solo tenía una tabla, con thead para los días y tbody para el grid del calendario; pero una vez que empecé a escribir el Javascript para moverse entre meses, me dí cuenta que necesitaba usar algo más flexible. Vas a ver por que cuando lleguemos al JavaScript.

Entonces, pon esto en el header:

Tenemos cinco elementos aquí; en la parte de afuera, tenemos los botones para moverse entre meses izquierdo y derecho; como no quería usar imágenes en éste proyecto, encontré las HTML entities &lang; y &rang; ( < y > , respectivamente ). Luego, tenemos dos span vacíos para los ganchos del calendario. Finalmente, tenemos la etiqueta mes/año en el medio.

El contenido para la table#days es bastante simple:

Finalmente, tenemos el contenido de div#cal-frame; míralo y luego lo comentamos.


Screencast completo



Así que qué tenemos aquí? Básicamente, estamos creando el grid del calendario con una tabla (después, vamos a insertar el mes actual dinámicamente). Las celdas apropiadas tienen los días del mes; si las celdas son vacías, tienen la clase "nil"; finalmente, la fecha de hoy tiene la clase "today".

Y realmente, eso es todo el HTML; no hay mucho que ver ahorita, pero esto es lo que tenemos:



Paso 2. El CSS

Empecemos con los contenedores:

Luego nos enfocamos en la barra de encabezado: Luego de establecer un color de fondo, estamos centrando el calendario horizontalmente y dándole sombra con box shadow. Por supuesto, la tipografía. Pero por qué establecemos display para la tabla? Por defecto, un div va a desplegarse en block, lo que significa que va a abarcar todo el ancho disponible; al desplegarlo como tabla, va a abarcar el ancho mínimo que pueda (manteniendo a los hijos), y seguir siendo un elemento block.

Luego, enfoquémonos en la barra de encabezado:

Esta es la primera parte del estilo del encabezado; empezamos por establecer el cursor como puntero; de esta manera, el texto no va a parecer que se puede seleccionar. Luego, establecemos el fondo color rojo; sin embargo, si el navegador lo soporta, vamos a usar una gradiente de fondo: no olvides agregarlo para mozilla y webkit! Luego, establece la altura a 34px; vamos a establecer la posición relativa, porque los hijos van a estar posicionados absolutamente; al posicionar el elemento padre relativo, los hijos van a ser posicionados absolutamente respecto al padre, en vez del body. Establece el color del texto a blanco, redondea las esquinas superiores izquierda y derecha y cambia la fuente a bold. Luego, dale una sombra sutil, para hacer que el texto se vea metido. Finalmente, transforma el texto a mayúsculas.

Cada uno del os items del header es un span; cada uno de estos va a ser mostrado como inline block. También, dale una altura de línea (line-height) de 34px (la altura del encabezado).

Estos spans también tienen algunas clases especiales, así que veamos esto:

Primero, tenemos la clase "hook"; recuerda, estos son los "ganchos" o "aros" del calendario. Vamos a establecer ancho y alto. Luego, lo posicionamos absolutamente. Luego, lo movemos hacia arriba desde el fondo 60%. Vamos a redondear las esquinas lo suficiente para hacer que los ganchos se vean redondos. Luego, vamos a establecer un color de fondo; si el navegador soporta gradientes, vamos a sobreescribir el color sólido con una gradiente. Luego, vamos a darle una sombra.

Vamos a usar las clases de posicionamiento para disponer los ganchos horizontalmente; si el elemento tiene las clases "hook" y "right", lo movemos 15% a la derecha; si tiene la clase "left", lo movemos 15% desde la izquierda.

Ahora tenemos los botones para mover los meses:

Vamos a establecer el ancho en estos botones y centrar el texto; por supuesto, vamos a necesitar posicionarlos absolutamente. Luego, para el botón de la izquierda, lo movemos completamente a la izquierda y redondeamos la parte superior izquierda. Para el botón derecho, todo hacia la derecha y redondeamos la esquina superior derecha.

Finalmente, vamos a añadir efectos al mover el mouse por encima; por supuesto, vamos a usar una gradiente.

Hay un elemento más que estilizar: la etiqueta del mes.

Vamos a usar letter-spacing para darle a los caracteres un poco más de espacio. Luego, le damos al span un ancho de 100% y centramos el texto. Como todos los elementos hermanos están posicionados absolutamente, dándole ancho completo se ve exactamente como queremos.

Así que ese es el encabezado completo! Debo mencionar que aunque posicionamos la mayoría de elementos de forma absoluta, como usamos porcentajes para posicionarlos, todo escala perfectamente cuando se aumente o disminuya el tamaño de letra del navegador.

Muy bien, vamos ahora a los nombres de los días.

Empezamos con selectores un poco más genéricos: el encabezado de los días y el grid del calendario son tablas ambos, así que ésta es la primera regla que aplicamos a ambos: establecemos el fondo blanco y colapsamos los bordes. Cuando los bordes de las tablas se colapsan, no tienen márgen entre ellos y las células adyacentes comparten los bordes. Después, para todas las celdas de la tabla, le damos un color de texto, establecemos el ancho, alto y altura de línea a 30px, y centramos el texto. Todas con borde y el cursor por defecto (flecha);

Luego, vamos a añadir un poco de estilos específico para las celdas de la tabla en la tabla de los días: vamos a reducir su altura y altura de línea un poco, asegurarnos de que sea mayúscula, y restablecer el tamaño de la fuente y el color del texto. (Nota: en el screencast, escribí #day en vez de #days en el selector para el tercer bloque más arriba y nunca lo corregí; asegúrate de ponerlo bien!)

Para qué es la última regla más arriba? Bueno, ahorita, hay bordes gris claro en las celdas de los nombres de los días. Queremos cambiar el color de los bordes de la derecha a blanco, para que no se vean. Sin embargo, no queremos hacer esto para la última celda de la fila. Así que podemos usar dos pseudo-clases. :not va a excluir el selector que recibe como "parámetro". :last-child toma el último hijo de los elementos que ya hemos seleccionado: en éste caso, las celdas de la tabla. Luego, sólo establecemos el borde derecho blanco.

Estas dos reglas están dirigidas al grid del calendario. Para la celda de tabla con la clase "today", cambiamos el fondo a gris claro y el texto a gris oscuro. Después, ponemos una sombra: es una sombra blanca, pero no usamos ningún difuminado, así que es una línea blanca; estamos empujándola hacia arriba y hacia la derecha un píxel, así obtenemos el efecto de un segundo borde. Nota que estamos agregando "inset" a la declaración de la sombra, así la sombra está dentro de la celda.

La siguiente regla aplica un efecto al pasar el mouse sobre las celdas de la tabla en el grid del calendario, excepto aquellos con la clase "nil"; cambiamos el texto a blanco y agregamos una sombra para el texto. Luego, cambiamos el fondo a rojo, usando gradientes cuando podamos. Incluímos la regla para remover la sombra específicamente para la celda "today".

Hay un caso especial que no hemos mencionado aún; tome un calendario - no, no iCal, me refiero a un calendario de papel - y fíjese, digamos, en octubre de 2010. Vas a notar que en la última semana hay celdas divididas, con el 24 y el 31 en el mismo cuadro. Vamos a necesitar hacer eso, así que vamos a estilizarlo.

La forma en que marcamos eso es poniendo cada fecha en un span dentro de la celda.

Primero, posicionamos los span relativamente y reducimos su fuente sólo un poco; Luego, movemos el primero 5px hacia arriba y el segundo 5px hacia abajo.

Vamos a hacer una cosa más; cuando cambiamos de mes, queremos desvanecer de uno al otro; esto requiere que las dos tablas estén una sobre la otra. Podemos realizar eso con esto:

La que estamos desvaneciendo va a tener una clase "temp"; la que estamos por mostrar (por un momento) va a tener la clase "curr".

Y eso es todo el CSS! Ahora sigamos con un poco de funcionalidad.


Paso 3. El JavaScript

Vamos a hacer la funcionalidad de nuestro calendario que sea fácilmente reusable; en ese sentido, vamos a empezar con este esqueleto:

Así que creamos tres funciones dentro de nuestra función CALENDAR; una va a arrancar el módulo de calendario, la segunda va a cambiar entre meses, y la tercera va a crear el grid del calendario; nota la línea que está después: createCal.cache = {}; vamos a comentar eso también!

Creamos también tres variables al inicio: vamos a darles valor a wrap y label dentro de init, meses es un arreglo con los nombres de los meses.

Este es el contenido de nuestra función init:

Empezamos por darle a wrap y label los valores apropiados: nota que usamos el selector pasado a init como parámetro para encontrar wrap, pero usamos "#cal" si no se provee. Luego, ligamos eventos click a los botones de anterior y siguiente; estos llaman a la función switchMonth; si queremos el siguiente calendario, pasamos true, si no, pasamos false.

Sin embargo, switchMonth también puede recibir dos parámetros más; vamos a usar esos para el evento click de la etiqueta de mes y año. Cuando el usuario hace click en el nombre del mes, vamos a cambiar al mes actual; así que vamos a pasar el mes y año actuales, que podemos obtener del objeto de JavaScript Date. No olvides establecer el parámetro next en nulo!

Una cosa más (y un tip extra, que no está en el screencast!): Cuando el usuario carga la página, queremos cargar el mes correcto sobre el mes que está codificado por defecto. La manera más simple de hacer eso es llamando al método click de jQuery en la etiqueta sin ningún parámetro; esto simula un click de mouse, y muestra el calendario con el mes actual.

Sigamos con la función switchMonth:

Vamos a darles valor a algunas variables en la parte superior; vamos a separar la etiqueta en un arreglo llamado curr; también estamos creando una variable calendar y tomando el año del calendario que se está mostrando.

Luego las cosas se ponen más complicadas. He usado operadores condicionales de JavaScript aquí, de esta manera puedo poner todo en una sola línea. En vez de tratar de explicarlo, fíjate en esto, esto es lo que esas líneas están haciendo.

Puedes ver por que el operador condicional es atractivo: es una sola línea. Esta es la versión expandida de la variable year:

Al final de todo, month y year van a tener los valores correctos para el calendario que estamos tratando de mostrar al usuario. Si te sientes más cómodo, puedes reemplazar esas dos líneas con los bloques de código más arriba.

Luego creamos el calendario y ajustamos el DOM como corresponde:

Qué hay en el objeto calendario que se obtiene de la función createCal? Es un objeto como este:

Vamos a discutir por qué la propiedad calendar es un método cuando construyamos el objeto. Por ahora, regresemos a ponerlo en la página. Vamos a obtener el frame del calendario y encontrar el calendario que se está mostrando actualmente. Luego, vamos a remover la clase "curr" y aplicar la clase "temp"; para poner el nuevo calendario (el cual, por cierto, viene con la clase "curr"), y desvanecer y remover el antiguo.

Bueno, ahora solo nos falta una función: createCal.

Así empezamos: las variables. Tenemos day, fijado en 1; tenemos 2 variables para iterar: i y j. Luego, descubrimos qué día de la semana el mes empieza; podemos hacer esto creando un objeto Date para el primer día del mes y llamando a getDay.

Luego creamos un arreglo que guarda el número de días en cada mes; para febrero, tenemos que tener en cuenta los años bisiestos, así que usamos otra expresión ternaria para calcular eso.

Finalmente, tenemos la muy importante variable calendar, que es un arreglo.

Seguido, queremos usar la propiedad cache que pusimos en la función createCal. (Como todo en JavaScript es un objeto, incluso las funciones pueden tener propiedades.)

Esto es lo que está pasando: hay una posibilidad de que el usuario va a "pedir" el mismo mes más de una vez. Una vez que lo creamos la primera vez, no hay necesidad de crearlo otra vez; lo guardamos en cache y lo usamos después.

Si ese objeto cache tiene una propiedad con el año que estamos buscando, podemos buscar la disponibilidad del mes; si ya construimos ese mes, retornamos el objeto en cache. Si no hay propiedad para el año, la hacemos, porque necesitamos poner el mes que estamos por crear.

Si pasamos este punto, entonces necesitamos empezar a crear el calendario para el mes solicitado.

Esto es un poco complicado; mientras haveDays es true, sabemos que tenemos días restantes en el mes. De esta forma, vamos a usar el iterador i para agregar un arreglo de semana a nuestro arreglo del calendario. Luego, usamos un for con nuestro iterador j, mientras sea menor a 7; pues empezamos con 0, esto nos va a dar 7 días para nuestro arreglo semanal. Dentro de nuestro for, hay tres casos.

Primero, necesitamos corroborar si estamos en la primer semana del mes; si es así, no necesariamente empieza en el primer día. Ya sabemos qué día empieza el mes; eso está en nuestra variable startDay. De esta manera, si j === startDay, estamos en el día correcto para empezar, así que vamos a poner el valor day en el lugar correcto. Luego incrementamos day y startDay en uno. La siguiente pasada por el for, j y startDay van a ser iguales, así que eso va a seguir funcionando para el resto de la semana.

Si no estamos en la primer semana (i !== 0), entonces nos vamos a asegurar que todavía nos quedan días por agregar al calendario; si es así, los colocamos en el lugar correcto. Finalmente, si no estamos en la primer semana y no nos quedan más días para agregar al mes, vamos a poner un string vacío. Luego, vamos a establecer haveDays como false.

Al final, vamos a corroborar si day es mayor que el número de días en el mes; si es así, vamos a establecer haveDays false. Esto es para el caso especial en que el mes termina en sábado.

Por supuesto, no olvides incrementar i justo afuera del for!

No queremos que nuestro calendario tenga más de 5 semanas; si un día o dos terminan en la semana 6, vamos a partir la celda en la semana 5, como lo hemos preparado en el CSS. Así, si hay un sexto arreglo dentro del arreglo del calendario, vamos a iterar sobre él. Luego, si el contenido del elemento del arreglo no es un string vacío, vamos a reasignar el valor de la celda directamente arriba de la fila 6: vamos a encerrar ese valor en un span y concatenar otro span con el valor correspondiente de la fila 6.  Tiene sentido, verdad?

Una vez que tenemos todo colocado, vamos a cortar el último elemento fuera de calendar.

Ahora es tiempo de concatenar cada semana en nuestro calendar; vamos a iterar sobre cada semana en un for y convertir las entradas en filas de tabla (tr) con cada día dentro de una celda (td). Luego, vamos a convertir todo en un objeto jQuery, después de poner todas las filas de tabla juntas y contenerlas dentro de una tabla. Vamos a añadir la clase "curr" a esa tabla.

Todas las celdas vacías (podemos usar el pseudo-selector de jQuery :empty para encontrarlas), se les pone la clase "nil".

Si estamos creando un calendario para el mes actual, vamos a encontrar la celda del día de hoy y darle la clase "today"; podemos encontrarla pasando una función al método filter de jQuery. La función retorna true si el texto de la celda coincide con la fecha.

Finalmente, vamos a crear nuestro objeto terminado y ponerlo en el cache. Por qué estamos haciendo la propiedad calendar una función? Bueno, si acabamos de retornar un objeto de jQuery, una vez que lo agregamos al calendario y nos movemos a otro mes, la tabla sería removida del DOM; después, si regresamos a ese mes, el elemento no se mostraría por que el cache está referenciando ese mismo elemento del DOM. Así que usamos el método clone de jQuery para retornar una copia del elemento en el DOM. Luego, a label se le asigna el nombre del mes usando el arreglo de meses y se le concatena el año. Finalmente, retornamos el objeto.

Estamos listos! De vuelta a index.html, vamos a agregar un script tag de ésta manera:

Eso es todo! Así es como se ve nuestro producto terminado!


Pero no puedo mostrarte la funcionalidad, vas a tener que ver el código por ti mismo! Gracias por leer!

¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.