Entre las nuevas características de HTML5 están los nuevos tipos de input
, para campos numéricos, de fecha, url, email, etc. El problema al crear aplicaciones es cómo utilizarlos sin dejar de lado los navegadores que no las soportan.
Debemos aceptar el hecho de que hay personas que utilizan navegadores desactualizados, o que por las limitaciones de sus sistemas operativos no pueden actualizarlos. Poco podemos hacer respecto a esto, pero eso no significa que desaprovechemos las características que todo usuario tiene ya a disposición, por el bien de nuestra aplicación.
Un caso en particular es el del input de tipo date
. Los navegadores que soportan este tipo de elemento lo presentan como un cuadro de texto que al seleccionarlo, muestra un calendario para facilitarle al usuario la selección de una fecha.
El formato de la fecha también lo establece automáticamente el navegador, lo cual nos evita tener que determinarlo nosotros, además de que el mismo navegador se encarga de que lo introducido por el usuario sea únicamente una fecha.
Para navegadores que no soportan HTML5
Ya he mencionado Modernizr antes, y en este caso lo recomiendo muchísimo, ya que nos evita tener que validar por nuestra propia cuenta el que un elemento de las nuevas tecnologías Web sea soportado por el navegador. Simplemente preguntando a Modernizr si el navegador soporta el tipo date, lo aplicamos, y si no, hacemos un fallback.
Para el caso del input de tipo date, solo ejecutaremos una función en caso de que el tipo no sea soportado.
El widget para fecha que estoy utilizando es el datepicker de jQuery UI.
$(document).on('ready',function(){ if(Modernizr.inputtypes.date===false) $('input[type=date]').datepicker(); })
Al terminar de cargarse la página, si no se soporta el tipo, aplicamos el datepicker para todo elemento que tiene el atributo type="date"
. Con esto, todos los elementos de tipo fecha tendrán un calendario como ayuda al usuario, a menos que el navegador sea lo suficientemente primitivo para no soportar ni siquiera jQuery 😛
Un momento… ¡Pero si «date» no está soportado! ¿¡Cómo es que esto funciona!?
Cuando creamos una página web, definimos etiquetas HTML, cada una con sus atributos necesarios. El navegador, al leer la página, construye el árbol DOM, que es la estructura de elementos que conforman el contenido de la página. Estos elementos coinciden con la representación de las etiquetas definidas, y los atributos que le dimos a cada etiqueta son convertidos en las propiedades de los elementos equivalentes.
Lo que determina si un navegador soporta o no un elemento o propiedad es el que éste se encuentre dentro del árbol DOM. Podemos agregar una etiqueta <header>
a nuestra página, y si el navegador la soporta o no, la considerará dentro del árbol DOM, dentro de la estructura de elementos y para la aplicación de estilos. Pero si no la soporta, esto es independiente de que la hayamos agregado a la página. La etiqueta está ahí, y el navegador no la verá como texto. Simplemente no puede hacer nada por ella.
Podemos poner <input type="date"/>
en nuestra página. Si el navegador lo soporta, creará el objeto de tipo HTMLInputElement
con su propiedad type=»date». Si no lo soporta, el objeto HTMLInputElement tendrá type=»text». ¿Por qué text? Porque es el tipo predeterminado para un input que no tiene el atributo definido, o que no tiene uno de los que el navegador reconoce. Sea como sea, ningún cambio de estos afectará en absoluto a la etiqueta. El objeto input en el DOM podrá tener type=»text», pero la etiqueta input en el HTML seguirá teniendo type=»date».
Ahora, los selectores de jQuery, al igual que los de CSS y los del QuerySelector de JavaScript, incluyen [atributo]
y [atributo=valor]
, el primero para buscar los elementos que tengan tal atributo definido, sin importar su valor, y el segundo para buscar los elementos en los que tal atributo tiene tal valor.
Todo esto se resume en que buscar input[type=date] nos devolverá todos los elementos cuyo atributo type en HTML sea igual a date, independientemente del que resultó en el objeto del árbol DOM.
Obtener el valor de la fecha
Algo que notaremos rápidamente es que el formato de la fecha no lo podemos editar, y el navegador lo definirá por sí mismo. El navegador podrá tomar el formato de la configuración del sistema, del idioma definido para el mismo programa, poner un formato fijo, o como sea, pero no podemos cambiarlo. De cualquier forma, esto no será necesario.
En el objeto input (el del árbol DOM) la propiedad value
guardará el valor de la fecha seleccionada, de la misma forma que cualquier otro tipo de input, y será el mismo texto que tenemos en pantalla. Esto no nos es muy útil, pues no podemos saber qué formato manejará cada navegador, y probablemente en uno veremos 10/05/2012
y en otro veremos 2012-05-10
. Así que value no nos sirve para las fechas.
La propiedad que nosotros necesitamos, y que es nueva en HTML5 es valueAsDate
, la cual está hecha específicamente para los tipos date, datetime, etc. Para obtener o asignar el valor, soporte o no soporte HTML5, podemos hacer esto:
var fecha; var input = $('#fecha'); if (Modernizr.inputtypes.date===true) { fecha = input.prop('valueAsDate'); // o input[0].valueAsDate } else { fecha = input.datepicker('getDate'); }
Cualquiera de las dos que usemos nos devolverán un objeto Date con el valor asignado al input.
Afectación por zona horaria
Esto que voy a mencionar es un «problema» en el input de tipo date de HTML5. En widgets como el datepicker de jQuery UI no pasa esto.
Si en un input de tipo date selecciono la fecha de hoy, veré que su valor es 31/10/2012. Pero si reviso el valor desde JavaScript, veré esto:
30/10/2012 17:00:00
¿Por qué me puso la fecha de ayer? Y además ¿por qué le puso las 17 horas si yo solo selecciono la fecha?
Esto es por la zona horaria. Yo seleccioné el 31 de octubre, y el navegador así lo considera, pero para la hora UTC, es decir, en la zona horaria 0 GMT. Por lo tanto, si estoy en la zona -7 GMT, le resta siete horas a la fecha que seleccioné y queda como el 30 de octubre a las 5:00 PM.
Qué bueno que esto queda claro. Pero la fecha que nos muestra sigue sin ser la seleccionada. Si esta fecha la paso a un servicio web, por ejemplo, recibirá el 30 de octubre, no el 31.
Para que podamos usar la fecha correcta, hay que sumarle las siete horas que se le quitaron. Dicho más correctamente (pues no siempre van a ser siete, como en mi caso), debo sumarle las horas omitidas por la diferencia de zonas horarias.
Un objeto Date tiene la función getTimezoneOffset()
, la cual nos devuelve la cantidad de minutos de diferencia entre una zona horaria y la zona 0 GMT. Por lo que esta cantidad de minutos hay que sumársela a la fecha.
var fecha; var input = $('#fecha'); if (Modernizr.inputtypes.date===true) { fecha = input.prop('valueAsDate'); // o input[0].valueAsDate fecha.setMinutes(fecha.getMinutes()+fecha.getTimezoneOffset()); } else { fecha = input.datepicker('getDate'); }
¿Y por qué no hacer todo con JavaScript y evitarme problemas?
Claro que sería más fácil para nosotros simplemente usar un componente de jQuery y puros input de tipo text. Pero hay que tomar en cuenta que los usuarios que tienen la posibilidad de usar un navegador moderno (cosa que hace diez años sería impensable, pero ya no) puedan aprovechar una característica nueva y mejor.
Y no es nomás por estar a la moda con el buen rollo de HTML5. El navegador que soporta el tipo date maneja nativamente un control visual para mostrar el calendario. El navegador que no lo soporta y usa jQuery, tiene que ejecutar un script en la página ya cargada, no exento de errores de código, que requiere actualizarse, y que además de ser un paso más que se tiene que ejecutar durante la carga, también requiere más definiciones de estilos, descarga de imágenes, etc.
En resumen, un componente visual en JavaScript consume más recursos del usuario que uno incluido en el navegador. Además de que el trabajo de mantenimiento corre directamente por cuenta del desarrollador.
Con la forma que propongo tendremos que escribir un par de líneas más, pero son cambios positivos, a favor de nosotros, de nuestro cliente, y (creo) principalmente, de la aplicación.