Selector de fecha en JavaScript vanilla

Selector de fechas (date picker) en JavaScript puro, sin librerías externas. Muestra un calendario mensual navegable con botones de mes anterior y siguiente, marca el día actual y permite elegir cualquier fecha. La semana empieza el lunes y la fecha seleccionada se muestra en formato largo en español.
				<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Selector de fecha — Date Picker en JavaScript vanilla</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
font-family: system-ui, -apple-system, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #f5f7fa;
gap: 1.5rem;
padding: 2rem;
}

.dp-wrapper {
background: white;
border-radius: 14px;
box-shadow: 0 4px 24px rgba(0,0,0,.10);
width: 300px;
overflow: hidden;
font-size: 14px;
user-select: none;
}

.dp-header {
background: #2563eb;
color: white;
display: flex;
align-items: center;
justify-content: space-between;
padding: .8rem 1rem;
}

.dp-header button {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 1.4rem;
line-height: 1;
padding: .2rem .5rem;
border-radius: 6px;
transition: background .15s;
}

.dp-header button:hover { background: rgba(255,255,255,.2); }

.dp-month-year {
font-weight: 700;
font-size: .95rem;
text-transform: capitalize;
}

.dp-weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
padding: .6rem .75rem .2rem;
color: #6b7280;
font-size: .72rem;
font-weight: 700;
text-align: center;
text-transform: uppercase;
}

.dp-days {
display: grid;
grid-template-columns: repeat(7, 1fr);
padding: .3rem .75rem .75rem;
gap: 2px;
}

.dp-day {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 50%;
border: none;
background: none;
font-size: .82rem;
transition: background .12s, color .12s;
font-family: inherit;
}

.dp-day:not(.empty):hover { background: #eff6ff; }
.dp-day.today { font-weight: 800; color: #2563eb; }
.dp-day.selected { background: #2563eb !important; color: white; }
.dp-day.empty { visibility: hidden; cursor: default; }

.dp-footer {
border-top: 1px solid #f3f4f6;
padding: .5rem .75rem;
display: flex;
justify-content: flex-end;
}

.dp-clear {
background: none;
border: 1px solid #e5e7eb;
border-radius: 6px;
padding: .25rem .7rem;
cursor: pointer;
font-size: .78rem;
color: #6b7280;
transition: border-color .15s, color .15s;
font-family: inherit;
}

.dp-clear:hover { border-color: #9ca3af; color: #374151; }

#resultado {
font-size: 1rem;
color: #1f2937;
background: white;
padding: .6rem 1.4rem;
border-radius: 8px;
box-shadow: 0 1px 6px rgba(0,0,0,.08);
}
</style>
</head>
<body>

<div id="datepicker"></div>
<p id="resultado">Ninguna fecha seleccionada</p>

<script>
/*
* Date Picker — selector de fecha en JavaScript puro
* Sin dependencias. Semana empieza el lunes. Idioma español.
*
* Código mejorado por David Carrero — https://carrero.es
* Fecha de modificación: 2026-05-09
*
* Uso básico:
* new DatePicker('#contenedor', { onChange: fecha => console.log(fecha) });
*
* El parámetro onChange recibe un objeto Date con la fecha elegida,
* o null si el usuario pulsa "Limpiar".
*/
class DatePicker {
#selected = null;
#current;
#today;

static DIAS = ['Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sá', 'Do'];
static MESES = [
'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'
];

constructor(selector, { onChange } = {}) {
this.root = document.querySelector(selector);
this.onChange = onChange ?? null;
this.#today = new Date();
this.#current = new Date(this.#today.getFullYear(), this.#today.getMonth(), 1);
this.#render();
}

/* ?? renderizado ??????????????????????????????? */

#render() {
this.root.innerHTML = this.#html();
this.root.querySelector('[data-prev]').onclick = () => this.#move(-1);
this.root.querySelector('[data-next]').onclick = () => this.#move(+1);
this.root.querySelector('[data-clear]').onclick = () => this.#clear();
this.root.querySelectorAll('[data-date]').forEach(btn =>
btn.onclick = () => this.#pick(new Date(btn.dataset.date))
);
}

#html() {
const y = this.#current.getFullYear();
const m = this.#current.getMonth();
return `
<div class="dp-wrapper">
<div class="dp-header">
<button data-prev aria-label="Mes anterior">&#8249;</button>
<span class="dp-month-year">${DatePicker.MESES[m]} ${y}</span>
<button data-next aria-label="Mes siguiente">&#8250;</button>
</div>
<div class="dp-weekdays">
${DatePicker.DIAS.map(d => `<span>${d}</span>`).join('')}
</div>
<div class="dp-days">${this.#cells(y, m)}</div>
<div class="dp-footer">
<button class="dp-clear" data-clear>Limpiar</button>
</div>
</div>`;
}

#cells(y, m) {
const first = new Date(y, m, 1);
const last = new Date(y, m + 1, 0).getDate();
const padding = (first.getDay() + 6) % 7; // lunes = columna 0
const cells = Array(padding).fill(
'<button class="dp-day empty" tabindex="-1" aria-hidden="true"></button>'
);

for (let d = 1; d <= last; d++) {
const date = new Date(y, m, d);
const classes = ['dp-day'];
if (this.#sameDay(date, this.#today)) classes.push('today');
if (this.#sameDay(date, this.#selected)) classes.push('selected');
cells.push(
`<button class="${classes.join(' ')}" data-date="${date.toISOString()}"
aria-label="${d} de ${DatePicker.MESES[m]} de ${y}">${d}</button>`
);
}
return cells.join('');
}

#sameDay(a, b) {
return b != null &&
a.getDate() === b.getDate() &&
a.getMonth() === b.getMonth() &&
a.getFullYear() === b.getFullYear();
}

/* ?? acciones ?????????????????????????????????? */

#move(delta) {
this.#current.setMonth(this.#current.getMonth() + delta);
this.#render();
}

#pick(date) {
this.#selected = date;
document.getElementById('resultado').textContent =
date.toLocaleDateString('es-ES', {
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
});
this.onChange?.(date);
this.#render();
}

#clear() {
this.#selected = null;
document.getElementById('resultado').textContent = 'Ninguna fecha seleccionada';
this.onChange?.(null);
this.#render();
}
}

/* Inicialización */
new DatePicker('#datepicker', {
onChange: fecha => {
if (fecha) console.log('Fecha elegida:', fecha.toISOString().slice(0, 10));
}
});
</script>

</body>
</html>
Descargar adjuntos
COMPARTE ESTE TUTORIAL

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
TUTORIAL ANTERIOR