Introducción
Una interfaz responde a lo que hace la persona: pulsa un botón, escribe, mueve un control, usa el teclado. Cada una de esas acciones es un evento, y JavaScript puede escucharlo y reaccionar.
Una interfaz responde a lo que hace la persona: pulsa un botón, escribe, mueve un control, usa el teclado.
addEventListener(tipo, función) ejecuta la función cada vez que ocurre el evento.
click, input, change, submit y keydown cubren casi todo.
El objeto evento da target, key y preventDefault().
Botones reales, teclado, estados con ARIA y respeto a prefers-reduced-motion.
Una interfaz responde a lo que hace la persona: pulsa un botón, escribe, mueve un control, usa el teclado. Cada una de esas acciones es un evento, y JavaScript puede escucharlo y reaccionar.
<button type="button" id="saludar">Saludar</button> <p id="mensaje"></p>
const boton = document.querySelector("#saludar"); const mensaje = document.querySelector("#mensaje"); boton.addEventListener("click", () => { mensaje.textContent = "¡Hola!"; });
addEventListener(tipo, función) significa: "cuando ocurra este tipo de evento en este elemento, ejecuta esta función". La función no se ejecuta al cargar la página, sino cada vez que ocurre el evento.
También puedes pasar una función con nombre, lo que hace el código más legible:
EventoCuándo ocurreEjemplo de uso clickAl pulsar (con ratón, dedo, o teclado en un botón)Botones, pestañas, filtros inputCada vez que cambia el valor de un campoBuscador en vivo, sliders, selector de color changeAl terminar de cambiar un campo (al soltar, al salir)Casillas, desplegables submitAl enviar un formularioValidar antes de enviar keydownAl pulsar una teclaCerrar con Escape, atajos pointerenter / pointerleaveAl entrar o salir el punteroEfectos que no dependan solo de esto
La función recibe un objeto con información de lo que ha pasado. Se suele llamar evento o e:
document.addEventListener("keydown", (evento) => { if (evento.key === "Escape") { cerrarMenu(); } });
evento.target: el elemento donde ocurrió (el botón pulsado, el campo que cambió). evento.key: la tecla pulsada ("Enter", "Escape", "a"…). evento.preventDefault(): cancela lo que el navegador haría por defecto (por ejemplo, enviar el formulario y recargar la página).
Con querySelectorAll y forEach añades el mismo evento a varios elementos:
const filtros = document.querySelectorAll(".filtro"); filtros.forEach((boton) => { boton.addEventListener("click", () => { filtros.forEach((b) => b.classList.remove("activo")); boton.classList.add("activo"); }); });
Es el esqueleto de un filtro de proyectos.
Delegación de eventos
En la lección Componentes de interfaz con CSS dejaste el menú de móvil pendiente de JavaScript. Aquí está, accesible:
<button type="button" class="menu-boton" aria-expanded="false" aria-controls="menu"> Menú </button> <nav id="menu" class="menu" hidden> <a href="#trabajos">Trabajos</a> <a href="#sobre-mi">Sobre mí</a> <a href="#contacto">Contacto</a> </nav>
const botonMenu = document.querySelector(".menu-boton"); const menu = document.querySelector("#menu"); function alternarMenu() { const abierto = botonMenu.getAttribute("aria-expanded") === "true"; botonMenu.setAttribute("aria-expanded", String(!abierto)); menu.hidden = abierto; } botonMenu.addEventListener("click", alternarMenu); document.addEventListener("keydown", (evento) => { if (evento.key === "Escape" && !menu.hidden) { alternarMenu(); botonMenu.focus(); } });
aria-expanded avisa a los lectores de pantalla de si el menú está abierto. El atributo hidden oculta el menú para todos, también para el teclado. En pantallas grandes muestras el menú y ocultas el botón con CSS (@media (min-width: 48rem)), usando display para anular hidden.
Usa <button> para las acciones y <a> para ir a otra página. Un div con click no se puede usar con el teclado ni lo anuncia un lector de pantalla. Comprueba que todo funciona solo con el teclado (Tab, Intro, Espacio, Escape) y que el foco se ve. Comunica el estado: aria-expanded (desplegables), aria-pressed (botones de activar y desactivar), texto del botón que cambia. No dependas solo del paso del ratón (hover): en móvil no existe. Respeta a quien pide menos animación:
El navegador puede guardar pequeños textos que se mantienen al recargar la página:
localStorage.setItem("tema", "oscuro"); // guardar const tema = localStorage.getItem("tema"); // leer ("oscuro" o null) localStorage.removeItem("tema"); // borrar
Útil para recordar el tema claro u oscuro (actividad A0). Solo guarda texto, se queda en ese navegador y no es un lugar para datos personales ni contraseñas.
Un botón "Me gusta" que cuenta las pulsaciones y muestra el número. Un botón que alterna la clase oscuro en body y cambia su propio texto entre "Modo oscuro" y "Modo claro", con aria-pressed. Una lista de preguntas frecuentes: al pulsar cada pregunta (un button) se muestra u oculta su respuesta. Cierra el menú de ejemplo con Escape y comprueba que todo funciona sin ratón.
Escribir addEventListener("click", mostrar()): con paréntesis, la función se ejecuta al cargar, no al pulsar. Añadir el evento a la lista de querySelectorAll en lugar de a cada elemento. Olvidar type="button" en un botón dentro de un formulario: lo envía. Hacer clicables div o span en lugar de usar button. Escribir mal el tipo de evento ("onclick", "Click"): no da error, simplemente no pasa nada.
Una interfaz responde a lo que hace la persona: pulsa un botón, escribe, mueve un control, usa el teclado.
Código del tema: addEventListener
Una interfaz responde a lo que hace la persona: pulsa un botón, escribe, mueve un control, usa el teclado. Cada una de esas acciones es un evento, y JavaScript puede escucharlo y reaccionar.
<button type="button" id="saludar">Saludar</button>
<p id="mensaje"></p>const boton = document.querySelector("#saludar");
const mensaje = document.querySelector("#mensaje");
boton.addEventListener("click", () => {
mensaje.textContent = "¡Hola!";
});addEventListener(tipo, función) significa: "cuando ocurra este tipo de evento en este elemento, ejecuta esta función". La función no se ejecuta al cargar la página, sino cada vez que ocurre el evento.
También puedes pasar una función con nombre, lo que hace el código más legible:
function mostrarSaludo() {
mensaje.textContent = "¡Hola!";
}
boton.addEventListener("click", mostrarSaludo); // sin paréntesis: no la llames tú⚠️ Atención. No uses atributos como onclick="…" en el HTML. Mezclan contenido y comportamiento y son la forma antigua de hacerlo (las IA la proponen a menudo). Usa addEventListener en el fichero JavaScript.
| Evento | Cuándo ocurre | Ejemplo de uso |
|---|---|---|
click | Al pulsar (con ratón, dedo, o teclado en un botón) | Botones, pestañas, filtros |
input | Cada vez que cambia el valor de un campo | Buscador en vivo, sliders, selector de color |
change | Al terminar de cambiar un campo (al soltar, al salir) | Casillas, desplegables |
submit | Al enviar un formulario | Validar antes de enviar |
keydown | Al pulsar una tecla | Cerrar con Escape, atajos |
pointerenter / pointerleave | Al entrar o salir el puntero | Efectos que no dependan solo de esto |
La función recibe un objeto con información de lo que ha pasado. Se suele llamar evento o e:
document.addEventListener("keydown", (evento) => {
if (evento.key === "Escape") {
cerrarMenu();
}
});evento.target: el elemento donde ocurrió (el botón pulsado, el campo que cambió).evento.key: la tecla pulsada ("Enter", "Escape", "a"…).evento.preventDefault(): cancela lo que el navegador haría por defecto (por ejemplo, enviar el formulario y recargar la página).Con querySelectorAll y forEach añades el mismo evento a varios elementos:
const filtros = document.querySelectorAll(".filtro");
filtros.forEach((boton) => {
boton.addEventListener("click", () => {
filtros.forEach((b) => b.classList.remove("activo"));
boton.classList.add("activo");
});
});Es el esqueleto de un filtro de proyectos.
Delegación de eventos
Si los elementos se crean después con JavaScript, o son muchos, es más cómodo escuchar en el padre y mirar dónde se pulsó:
const barra = document.querySelector(".filtros");
barra.addEventListener("click", (evento) => {
const boton = evento.target.closest("button"); // el botón pulsado (o null)
if (!boton) return;
console.log("Filtro:", boton.dataset.categoria);
});En la lección Componentes de interfaz con CSS dejaste el menú de móvil pendiente de JavaScript. Aquí está, accesible:
<button type="button" class="menu-boton" aria-expanded="false" aria-controls="menu">
Menú
</button>
<nav id="menu" class="menu" hidden>
<a href="#trabajos">Trabajos</a>
<a href="#sobre-mi">Sobre mí</a>
<a href="#contacto">Contacto</a>
</nav>const botonMenu = document.querySelector(".menu-boton");
const menu = document.querySelector("#menu");
function alternarMenu() {
const abierto = botonMenu.getAttribute("aria-expanded") === "true";
botonMenu.setAttribute("aria-expanded", String(!abierto));
menu.hidden = abierto;
}
botonMenu.addEventListener("click", alternarMenu);
document.addEventListener("keydown", (evento) => {
if (evento.key === "Escape" && !menu.hidden) {
alternarMenu();
botonMenu.focus();
}
});aria-expanded avisa a los lectores de pantalla de si el menú está abierto.hidden oculta el menú para todos, también para el teclado.@media (min-width: 48rem)), usando display para anular hidden.<button> para las acciones y <a> para ir a otra página. Un div con click no se puede usar con el teclado ni lo anuncia un lector de pantalla.Tab, Intro, Espacio, Escape) y que el foco se ve.aria-expanded (desplegables), aria-pressed (botones de activar y desactivar), texto del botón que cambia.hover): en móvil no existe.El navegador puede guardar pequeños textos que se mantienen al recargar la página:
localStorage.setItem("tema", "oscuro"); // guardar
const tema = localStorage.getItem("tema"); // leer ("oscuro" o null)
localStorage.removeItem("tema"); // borrarÚtil para recordar el tema claro u oscuro (actividad A0). Solo guarda texto, se queda en ese navegador y no es un lugar para datos personales ni contraseñas.
oscuro en body y cambia su propio texto entre "Modo oscuro" y "Modo claro", con aria-pressed.button) se muestra u oculta su respuesta.Escape y comprueba que todo funciona sin ratón.addEventListener("click", mostrar()): con paréntesis, la función se ejecuta al cargar, no al pulsar.querySelectorAll en lugar de a cada elemento.type="button" en un botón dentro de un formulario: lo envía.div o span en lugar de usar button."onclick", "Click"): no da error, simplemente no pasa nada.