Eventos

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:

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.

Escuchar un evento

<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:

Los eventos más útiles

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

El objeto evento

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).

Varios elementos con el mismo comportamiento

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

Ejemplo completo: menú adaptable

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.

Interacción accesible

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:

Recordar una preferencia: localStorage

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.

Practica

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.

Errores frecuentes

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.

JavaScript
69

Eventos

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

📘 Teoría

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.

Escuchar un evento

<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.

Los eventos más útiles

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

El objeto evento

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).

Varios elementos con el mismo comportamiento

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);
});

Ejemplo completo: menú adaptable

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.

Interacción accesible

  • 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:

Recordar una preferencia: localStorage

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.

Practica

  1. Un botón "Me gusta" que cuenta las pulsaciones y muestra el número.
  2. Un botón que alterna la clase oscuro en body y cambia su propio texto entre "Modo oscuro" y "Modo claro", con aria-pressed.
  3. Una lista de preguntas frecuentes: al pulsar cada pregunta (un button) se muestra u oculta su respuesta.
  4. Cierra el menú de ejemplo con Escape y comprueba que todo funciona sin ratón.

Errores frecuentes

  • 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.

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros. culTest

La hice en febrero de 2026 para facilitar el aprendizaje de mis alumnos. La idea es aprender desarrollo web practicando y que el proyecto siga creciendo con nuevos temas, tests y retos.

Está inspirada en MDN, W3Schools, CodePen, Manz y muchos otros sitios de documentación sobre desarrollo web. Quería combinar teoría útil, ejemplos ejecutables, retos y el sistema de tests que ya tenía en culTest. culTest

Si te gustó, si no te gustó o si quieres escribirme, puedes hacerlo en cristianeslava@gmail.com