Artículo en vídeo
Datepicker es un calendario basado en JQuery integrado en un campo de formulario, con la finalidad de proporcionar al usuario un método cómodo y sencillo de insertar una fecha.
Cuando a un usuario se le pide insertar una fecha en un formulario web, si este no implementa unas reglas básicas o indicaciones claras, el usuario puede verse un poco perdido y no saber de qué forma insertarla:
¿Pongo el mes antes que el día?, ¿escribo el mes en números o letras?, ¿el año lo pongo con 2 o 4 cifras?, ¿separo las partes con guiones (-) o diagonales (/)?
Esto cobra aún más importancia si la fecha se va a guardar en una base de datos que espera que llegue de una forma concreta.
Para ello se implementaron herramientas como Datepicker, para que a la hora de insertar una fecha se desplegase un cómodo calendario en el que el usuario seleccionase cómodamente el mes, día y año sin errores, y todo se enviase a la base de datos debidamente procesado.
Actualmente, los navegadores web ya incorporan campos de tipo fecha <input type=»date»/>, como puedes ver en el siguiente campo:
Selecciona una fecha:
Cada navegador tiene su particular calendario, que como base, ayuda a que el usuario inserte una fecha correctamente, aunque de cara a funcionalidades más avanzadas están muy limitados.
Por ejemplo, no podemos enlazar dos campos de tipo fecha para que se comuniquen entre sí. Imagina que quieres incorporar un sistema de reserva de apartamentos en tu web, y quieres que cuando el usuario inserte la fecha de entrada, automáticamente la fecha de salida parta del día siguiente a la fecha de entrada, o que no se pueda escoger como fecha de salida, una fecha anterior a la de entrada. Con los calendarios basícos integrados por los navegadores actuales esto no es posible.
Pero con Datepicker si.
Para todo lo que requiera de una avanzada programación de fechas, Datepicker sigue siendo una excelente herramienta, y en este post vamos a ver cómo integrarlo en WordPress y cómo hacer las configuraciones más útiles dentro de cualquier sistema de reservas.
1. Descargar Datepicker
El primer paso es acudir a la web de descarga de Datepicker y descargar la última versión estable, que en el momento de escribir este post es la 1.12.1.
Tras descomprimir el ZIP, los archivos que nos interesa son:
- jquery-ui.css
- jquery-ui.structure.css
- jquery-ui.theme.css
- jquery-ui.js
- Además, crearemos un archivo .js para nuestras propias funciones JavasScript, al que llamaremos funciones.js.
Y la carpeta images que tendremos que subir al mismo directorio que los archivos CSS.
Mi recomendación es subirlos a la misma carpeta donde se encuentra el tema activo de WordPress, es decir /wp-content/themes/tema_activo/
Y dentro crear, si no existen, dos subdirectorios, css y js.
/wp-content/themes/tema_activo/css/ donde subiremos los archivos CSS y la carpeta images de Datepicker.
/wp-content/themes/tema_activo/js/ donde subiremos el archivo .js de Datepicker y nuestro archivo funciones.js.
De tal forma que la estructura de archivos de Datepicker quede así en nuestro proyecto:
- wp-content/themes/tema_activo/css/jquery-ui.css
- wp-content/themes/tema_activo/css/jquery-ui-theme.css
- wp-content/themes/tema_activo/css/jquery-ui.structure.css
- wp-content/themes/tema_activo/css/images/
- wp-content/themes/tema_activo/js/jquery-ui.js
- wp-content/themes/tema_activo/js/funciones.js
2. Declarar los archivos de Datepicker en WordPress
Una vez subidos todos los archivos, necesitamos declararlos en WordPress para que este los reconozca. Lo normal es hacerlo dentro del archivo de funciones (functions.php) del tema activo, pero si es un tema que se actualiza con frecuencia, también podemos crear nuestro propio plugin de funciones y declararlo ahí de la misma forma:
<?php
function js_calendarios(){
wp_enqueue_script('jquery');
wp_enqueue_style('calendario-css', get_template_directory_uri().'/css/jquery-ui.css', array(), '1.0');
wp_enqueue_style('calendario-css-2', get_template_directory_uri().'/css/jquery-ui.theme.css', array(), '1.0');
wp_enqueue_style('calendario-css-3', get_template_directory_uri().'/css/jquery-ui.structure.css', array(), '1.0');
wp_enqueue_script('calendario-js', get_template_directory_uri().'/js/jquery-ui.js', array('jquery'), '1.0');
wp_enqueue_script('funciones-js', get_template_directory_uri().'/js/funciones.js', array('jquery'), '1.0');
}
add_action('wp_enqueue_scripts', 'js_calendarios');
?>
3. Configuraciones básicas de Datepicker
Para personalizar nuestro calendario, nos vamos a dirigir a nuestro archivo funciones.js y escribimos:
$ = jQuery.noConflict();
$(document).ready(function(){
$(function(){
$("#datepicker1").datepicker();
});
});
El identificador #datepicker1 es el que tenemos que usar en el campo input donde queremos que aparezca el calendario, por ejemplo:
<input type="text" name="fecha_entrada" id="datepicker1" placeholder="Fecha de entrada" />
Imprimiría:
Hasta aquí no difiere mucho de la funcionalidad del calendario integrado por los propios navegadores, además por defecto está inglés, tanto las cadenas de texto como el hecho de que la semana comienza en Domingo.
Vamos a comenzar definiendo las etiquetas para que aparezca en español. Para ello tenemos que añadir una serie de parámetros a la función datepicker(), tal y como se muestra en el siguiente código:
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
});
Así conseguimos que nuestro calendario ahora se vea en español:
Pero nos falta un detalle más, y es hacer que la semana comience en Lunes en lugar de Domingo, para ello añadimos el parámetro:
firstDay: 1
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1, //Hace que la semana comience en Lunes
});
Y aquí el resultado:
Otra opción interesante es que no se pueda seleccionar un día anterior a la fecha actual, es decir, si hoy estamos a 10 de Diciembre de 2018, el calendario no nos va a permitir seleccionar una fecha anterior, para ello añadimos el parámetro:
minDate: 0
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1, //Hace que la semana comience en Lunes
minDate: 0, //Hace que no se puedan seleccionar fechas pasadas
});
Fijaos cómo los días anteriores aparecen en un color más transparente, indicando que no se pueden seleccionar:
Respecto al formato actual de la fecha, si seleccionamos un día en concreto veremos que el formato está definido en «mes/dia/año»:
Si queremos modificar el formato de fecha usamos el parámetro:
dateFormat: «dd/mm/yy»
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1, //Hace que la semana comience en Lunes
minDate: 0, //Hace que no se puedan seleccionar fechas pasadas
dateFormat: "dd/mm/yy", //Establece el formato de fecha
});
Cuyo resultado sería:
Otra opción que podría resultarnos de interés es la posibilidad de cambiar el mes y el año directamente, para ello usaríamos los parámetros:
changeMonth: true,
changeYear: true,
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1, //Hace que la semana comience en Lunes
minDate: 0, //Hace que no se puedan seleccionar fechas pasadas
dateFormat: "dd/mm/yy", //Establece el formato de fecha
changeMonth: true, //Permite seleccionar el mes directamente
changeYear: true, //Permite seleccionar el año directamente
});
El resultado:
4. Enlazando dos Datepickers
Hasta aquí hemos realizado configuraciones básicas, sobre todo para adaptar Datepicker al sistema de calendario español. Ahora veremos algo realmente interesante que consiste en enlazar dos calendarios Datepicker, de forma que cuando se seleccione una fecha en el Datepicker1, el Datepicker2 impida seleccionar una fecha anterior, muy interesante para sistemas de reservas.
Para ello vamos a declarar un segundo Datepicker al que llamaremos #datepicker2, con los mismos parámetros que el #datepicker1.
$("#datepicker2").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1, //Hace que la semana comience en Lunes
minDate: 0, //Hace que no se puedan seleccionar fechas pasadas
dateFormat: "dd/mm/yy", //Establece el formato de fecha
changeMonth: true, //Permite seleccionar el mes directamente
changeYear: true, //Permite seleccionar el año directamente
});
Ahora añadimos como parámetro al #datepicker1 el siguiente código:
onClose: function (selectedDate){
$("#datepicker2").datepicker("option", "minDate", selectedDate);
}
Y como parámetro del #datepicker2 el siguiente código:
onClose: function (selectedDate){
$("#datepicker1").datepicker("option", "maxDate", selectedDate);
}
Recuerda añadir una coma (,) al final si vas a seguir añadiendo parámetros.
Creamos un nuevo input:
<input type="text" name="fecha_salida" id="datepicker2" placeholder="Fecha de salida"/>
Y aquí el resultado, si seleccionamos como fecha de entrada el 25 de Diciembre, veremos que en fecha de salida no podemos seleccionar días anteriores al 25 de Diciembre:
Y ya para finalizar veremos una función para deshabilitar rangos de fechas, es decir, imagina que en tu web de reserva de apartamentos quieres que cuando el usuario despligue el calendario aparezcan bloqueados todos los días que esté reservado.
Para ello, previamente, tendríamos que hacer una consulta a la base de datos para traernos las fechas de entrada y salida de cada reserva y guardarlas en un vector o array:
Para la función siguiente necesitamos recoger las fechas de la base de datos y pasarlas a formato «mes/día/año».
En PHP es fácil:
<?php
$fecha_formateada = new DateTime($fecha_sin_formatear);
echo $fecha_formateada->format('m/d/Y');
?>
Cada grupo de fechas entrecomilladas representa un rango (fecha de entrada y fecha de salida), añadimos tantos rangos como reservas haya en la base de datos:
var RangeDates = ["01/10/2019,01/15/2019","01/17/2019,01/24/2019"];
var RangeDatesIsDisable = true;
Este ejemplo tomaría 2 rangos de fechas, compuestos por:
- Del 10 de Enero de 2019 al 15 de Enero de 2019.
- Del 17 de Enero de 2019 al 24 de Enero de 2019
Pasamos la siguiente función que hará las comprobaciones, desglosando los meses, días y años de cada fecha:
function DisableDays(date){
var isd = RangeDatesIsDisable;
var rd = RangeDates;
var m = date.getMonth();
var d = date.getDate();
var y = date.getFullYear();
for (i = 0; i < rd.length; i++){
var ds = rd[i].split(',');
var di, df;
var m1, d1, y1, m2, d2, y2;
if(ds.length == 1){
di = ds[0].split('/');
m1 = parseInt(di[0]);
d1 = parseInt(di[1]);
y1 = parseInt(di[2]);
if (y1 == y && m1 == (m + 1) && d1 == d) return [!isd];
}
else if (ds.length > 1){
di = ds[0].split('/');
df = ds[1].split('/');
m1 = parseInt(di[0]);
d1 = parseInt(di[1]);
y1 = parseInt(di[2]);
m2 = parseInt(df[0]);
d2 = parseInt(df[1]);
y2 = parseInt(df[2]);
if(y1 >= y || y <= y2){
if ((m + 1) >= m1 && (m + 1) <= m2){
if (m1 == m2){
if (d >= d1 && d <= d2) return [!isd];
}
else if(m1 == (m + 1)){
if (d >= d1) return [!isd];
}
else if(m2 == (m + 1)){
if (d <= d2) return [!isd];
}
else return [!isd];
}
}
}
}
return [isd];
}
Y por último añadimos el parámetro: beforeShowDay: DisableDays, a los dos Datepicker, quedando finalmente ambos así:
$("#datepicker1").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1,
minDate: 0,
dateFormat: "dd/mm/yy",
changeMonth: true,
changeYear: true,
beforeShowDay: DisableDays,
onClose: function (selectedDate){
$("#datepicker2").datepicker("option", "minDate", selectedDate);
}
});
$("#datepicker2").datepicker({
closeText: 'Cerrar',
prevText: '< Ant',
nextText: 'Sig >',
currentText: 'Hoy',
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr', 'May','Jun','Jul','Ago','Sep', 'Oct','Nov','Dic'],
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
firstDay: 1,
minDate: 0,
dateFormat: "dd/mm/yy",
changeMonth: true,
changeYear: true,
beforeShowDay: DisableDays,
onClose: function (selectedDate){
$("#datepicker1").datepicker("option", "minDate", selectedDate);
}
});
Como se aprecia al desplegar el calendario de fecha de entrada, ambos rangos aparecen deshabilitados:
Esto es solo un extracto de lo que considero es más importante y habitual configurar en cualquier sistema de reservas, pero en la documentación oficial de Datepicker encontrarás más funcionalidades, métodos y parámetros de configuración:
Espero que este post te haya sido de interés y utilidad, si es así no dudes en compartirlo o guardarlo en tus favoritos para futuras consultas.
Nos vemos en el siguiente post! 😉
Diego dice
Hola! Enhorabuena por el blog, se hace todo más fácil leyendo tus explicaciones, sobre todo para los no experimentados. En mi caso necesito únicamente deshabilitar todos los domingos del año. ¿Podrías indicarme cómo hacerlo? Muchas gracias!
Daniel, Gestionatuweb.net dice
Hola Diego, te enlazo este artículo a ver si puede ayudarte. Saludos.
https://stackoverflow.com/questions/4376987/disable-all-sundays-in-jquery-ui-calendar
Diego dice
Muchas gracias!
Eduar dice
Hola Daniel, excelente todo tu trabajo. Verdaderamente te agradezco todos tus aportes y sinceramente te felicito.
Tengo una inquietud, si tengo una tabla con el registro de fechas reservadas como puedo hacer para marcar esos días en el calendario e inhabilitarlos para futuras reservas.
Muchas gracias
dani dice
Hola Eduar, pues lo mismo que en el ejemplo, solo que en lugar de tener los rangos de fecha ya tomados, tendrás que traerlos previamente de la tabla con una consulta SQL, y pasarle el resultado al RangeDates[].
Pero para ello sí tendrías que tener algunos conocimientos de SQL, PHP y JavaScript.
Germán dice
Hola Dani!
Excelente explicación, justo lo que buscaba, para añadir el datepicker a mi formulario hecho en HTML en WordPress, que además también estoy montando en localhost, como en tu vídeo. Aunque estoy usando el theme Divi, el formulario lo he hecho con el módulo de código y funciona perfectamente, tan solo que me falta añadir el datepicker. Pero tengo un pequeño inconveniente, que la función get_template_directory_uri enlaza con las carpetas del tema padre y yo he creado las carpetas CSS y JS en el tema hijo, ya que así cuando tenga que hacer actualizaciones del el tema padre no se me pierde el trabajo. He buscado por internet y encontré que para enlazar un tema hijo hay que usar get_stylesheet_directory_uri, pero no sé cómo hacerlo correctamente, ya que simplemente reemplazándolo no me enlaza los archivos. Podrías por favor decirme cómo hacerlo correctamente? Me refiero al punto 2 de este tutorial, en el que hay que declarar los archivos de Datepicker en WordPress, en el archivo functions.php, ya que este código:
wp_enqueue_style(‘calendario-css’, get_stylesheet_directory_uri().’/css/jquery-ui.css’, array(), ‘1.0’); no me enlaza al child theme cuya ruta es /wp-content/themes/divi-child/css/jquery-ui.theme.css?ver=1.0.
dani dice
Hola, en efecto, para declarar un fichero CSS o JS en el child theme, hay que usar la función get_stylesheet_directory_uri().
A priori no entiendo por qué no lo toma en tu caso, seguro que hay algún siempre error.
Entiendo que tras declarar los archivos con wp_enqueue_style() en el caso de los CSS y wp_enqueue_script() en el caso de los JS, luego lo añades correctamente al hook wp_enqueue_scripts, verdad?
Haz una prueba, carga el frontend, visualiza el código fuente y busca si aparece tu archivo jquery-ur.theme.css en alguna parte del código. Si no aparece, es que no está apuntando bien al archivo en la declaración.
Borra también la caché de navegador, por si acaso.
Un saludo.
Germán dice
Hola Dani! gracias por tu respuesta. Cómo se hace eso?
«Entiendo que tras declarar los archivos con wp_enqueue_style() en el caso de los CSS y wp_enqueue_script() en el caso de los JS, luego lo añades correctamente al hook wp_enqueue_scripts, verdad?» me refiero a cómo añadirlo correctamente al hook wp_enqueue_scripts?
He comprobado lo que decías y el archivo jquery-ur.theme.css no aparece en el código
En el caso de los archivos .js si que enlazan bien con get_stylesheet_directory_uri().
Este es el contenido de mi archivo functions.php del child theme:
<?php
function my_theme_enqueue_styles() {
wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_styles' );
function js_calendarios(){
wp_enqueue_script('jquery');
wp_enqueue_style('calendario-css', get_stylesheet_directory_uri().'/css/jquery-ui.css', array(), '1.0');
wp_enqueue_style('calendario-css-2', get_stylesheet_directory_uri().'/css/jquery-ui.theme.css', array(), '1.0');
wp_enqueue_style('calendario-css-3', get_stylesheet_directory_uri().'/css/jquery-ui.structure.css', array(), '1.0');
wp_enqueue_script('calendario-js', get_stylesheet_directory_uri().'/js/jquery-ui.js', array('jquery'), '1.0');
wp_enqueue_script('funciones-js', get_stylesheet_directory_uri().'/js/funciones.js', array('jquery'), '1.0');
}
add_action('wp_enqueue_scripts', 'js_calendarios');
No se si hay algo mal?
Germán dice
He buscado un poco de información y ahora ya me carga calendario en el frontend, pero al tratarse de la web de un hotel, tengo dos campos en el formulario, fecha de entrada y otro para la fecha de salida y aunque, he puesto el id=»datepicker1″ en ambos campos, solo aparece en el primero, en el de fecha de entrada.