Un evento es algo que pasa en la página: un clic, una tecla, el envío de un formulario. Con addEventListener le dices al navegador qué función ejecutar cuando ocurra.
También te puede interesar
Un contador, una lista y un formulario
Para probarlo, guarda el código en un archivo .html, ábrelo en el navegador y abre la consola con F12 (en Mac, Cmd+Opción+J). Todos los ejemplos de esta página son un único archivo, sin nada más que instalar.
<!doctype html>
<html lang="es">
<head><meta charset="utf-8"><title>Eventos</title></head>
<body>
<button id="contador">Pulsado 0 veces</button>
<ul id="tareas">
<li>Comprar pan <button class="borrar">×</button></li>
<li>Llamar a Luis <button class="borrar">×</button></li>
</ul>
<form id="alta">
<input name="tarea" placeholder="Nueva tarea" required>
<button>Añadir</button>
</form>
<script type="module">
// 1. Un evento en un elemento
const boton = document.querySelector("#contador");
let veces = 0;
boton.addEventListener("click", () => {
veces++;
boton.textContent = `Pulsado ${veces} veces`;
});
// 2. Delegación: un solo escuchador para todos los botones de la lista,
// también para los que se añadan después
const tareas = document.querySelector("#tareas");
tareas.addEventListener("click", (evento) => {
const borrar = evento.target.closest("button.borrar");
if (!borrar) return;
const li = borrar.closest("li");
console.log("borro:", li.firstChild.textContent.trim());
li.remove();
});
// 3. Formularios: impedir que la página se recargue
const alta = document.querySelector("#alta");
alta.addEventListener("submit", (evento) => {
evento.preventDefault();
const datos = new FormData(alta);
const li = document.createElement("li");
li.append(datos.get("tarea") + " ");
const b = document.createElement("button");
b.className = "borrar";
b.textContent = "×";
li.append(b);
tareas.append(li);
console.log("añadida:", datos.get("tarea"));
alta.reset();
});
</script>
</body>
</html>Pulsamos tres veces el contador, borramos «Comprar pan», añadimos «Pagar la luz» con el formulario y la borramos después:
consolaborro: Comprar pan añadida: Pagar la luz borro: Pagar la luz
El botón acabó diciendo «Pulsado 3 veces», en la lista quedó solo «Llamar a Luis» y la dirección de la página no cambió: el formulario no la recargó.
Las tres ideas del ejemplo
La función recibe el evento. Es un objeto con los detalles de lo que pasó: evento.target es el elemento exacto donde ocurrió, y métodos como preventDefault() permiten cancelar lo que el navegador haría por su cuenta.
preventDefault() en el formulario. Al enviar un formulario, el navegador recarga la página o navega a otra. Si lo vas a gestionar con JavaScript, hay que impedirlo, y FormData recoge los valores de todos sus campos por su atributo name.
Delegación. En vez de poner un escuchador en cada botón «×», hay uno solo en la lista. Funciona porque el clic en un botón también llega a la lista que lo contiene; closest("button.borrar") averigua si venía de un botón de borrar. Y tiene una ventaja enorme: sirve también para las tareas que se añaden después, como «Pagar la luz», que no existía cuando se registró el escuchador.
Por qué funciona la delegación: captura y burbuja
<!doctype html>
<html lang="es">
<head><meta charset="utf-8"><title>Captura y burbuja</title></head>
<body>
<div id="fuera">
<div id="medio">
<button id="dentro">Púlsame</button>
</div>
</div>
<script type="module">
for (const id of ["fuera", "medio", "dentro"]) {
const el = document.getElementById(id);
el.addEventListener("click", (e) => console.log("captura:", id), { capture: true });
el.addEventListener("click", (e) =>
console.log("burbuja:", id, "| target:", e.target.id, "| currentTarget:", e.currentTarget.id));
}
document.getElementById("medio").addEventListener("click", (e) => {
if (e.altKey) {
e.stopPropagation();
console.log("medio detiene la burbuja");
}
});
document.getElementById("dentro").addEventListener("click", () => console.log("solo la primera vez"), { once: true });
</script>
</body>
</html>Un clic en el botón, luego otro, y un tercero con la tecla Alt pulsada:
consolacaptura: fuera captura: medio captura: dentro burbuja: dentro | target: dentro | currentTarget: dentro solo la primera vez burbuja: medio | target: dentro | currentTarget: medio burbuja: fuera | target: dentro | currentTarget: fuera --- segundo clic captura: fuera captura: medio captura: dentro burbuja: dentro | target: dentro | currentTarget: dentro burbuja: medio | target: dentro | currentTarget: medio burbuja: fuera | target: dentro | currentTarget: fuera --- con Alt captura: fuera captura: medio captura: dentro burbuja: dentro | target: dentro | currentTarget: dentro burbuja: medio | target: dentro | currentTarget: medio medio detiene la burbuja
Primero el evento baja desde fuera hasta el botón (captura), y después sube de vuelta (burbuja). En todos, target es el botón, pero currentTarget cambia: es el elemento cuyo escuchador se está ejecutando. Con { once: true }, el escuchador se borra solo tras la primera vez. Y stopPropagation(), en el tercer clic, detiene la burbuja en «medio»: «fuera» ya no se entera.
stopPropagation(). Cortar la burbuja rompe cualquier delegación que haya más arriba, incluida la de bibliotecas o herramientas de estadísticas. Casi siempre basta con comprobar event.target.El error frecuente: los paréntesis
<!doctype html>
<html lang="es">
<head><meta charset="utf-8"><title>Error frecuente</title></head>
<body>
<button id="saludar">Saludar</button>
<script type="module">
function saludar() {
console.log("¡hola!");
}
const boton = document.querySelector("#saludar");
boton.addEventListener("click", saludar()); // mal: la llama ahora mismo
console.log("página cargada");
</script>
</body>
</html>consola¡hola! página cargada (dos clics en el botón: no aparece nada más)
saludar(), con paréntesis, ejecuta la función en ese momento y le pasa a addEventListener lo que devuelve, que es undefined. Por eso «¡hola!» sale al cargar y los clics no hacen nada. Hay que pasar la función sin llamarla: addEventListener("click", saludar), o envolverla en una flecha si necesita argumentos: () => saludar("Ana").
Los eventos más usados
| Evento | Cuándo salta |
|---|---|
click | Clic o toque; también al pulsar Enter sobre un botón |
input | Cada vez que cambia el texto de un campo, letra a letra |
change | Al confirmar el cambio: salir del campo, elegir en una lista |
submit | Al enviar un formulario, con el botón o con Enter |
keydown | Al pulsar una tecla; evento.key dice cuál |
DOMContentLoaded | Cuando el HTML está leído, sin esperar a las imágenes |
Cada página de esta sección se abrió en Chromium 131, el motor de Chrome, controlado con Playwright, antes de publicarla; lo que aparece como consola es lo que imprimió de verdad. Documentación oficial: addEventListener en MDN.