Eventos en JavaScript

Los eventos permiten que tu página reaccione a acciones del usuario: clics, teclas, movimiento del mouse, envío de formularios y mucho más. Son la base de toda interfaz interactiva.

¿Qué es un evento?

Un evento es algo que ocurre en la página — el usuario hace clic, mueve el mouse, escribe en un input, o la página termina de cargar. JavaScript puede "escuchar" esos eventos y ejecutar una función cuando ocurran.

// La forma correcta: addEventListener
const boton = document.getElementById("mi-btn");

boton.addEventListener("click", function() {
    alert("¡Hiciste clic!");
});

// Con arrow function — más común
boton.addEventListener("click", () => {
    document.getElementById("resultado").textContent = "¡Clic recibido!";
});
Evitá los manejadores inline y onclick

<button onclick="hacer()"> y boton.onclick = fn funcionan, pero tienen limitaciones: solo podés asignar una función y mezclan HTML con JS. addEventListener es siempre la mejor opción.

Probá el Ejemplo 1: Eventos de click y teclado

Tipos de eventos más usados

Mouse

el.addEventListener("click",      fn);  // clic izquierdo
el.addEventListener("dblclick",   fn);  // doble clic
el.addEventListener("mouseenter", fn);  // el mouse entra al elemento
el.addEventListener("mouseleave", fn);  // el mouse sale del elemento
el.addEventListener("mousemove",  fn);  // el mouse se mueve dentro
el.addEventListener("contextmenu",fn);  // clic derecho

Teclado

document.addEventListener("keydown", (e) => {
    console.log(e.key);        // "Enter", "a", "ArrowUp", etc.
    console.log(e.code);       // "KeyA", "Enter", "Space"
    console.log(e.ctrlKey);    // true si Ctrl estaba apretado
    console.log(e.shiftKey);   // true si Shift estaba apretado

    if (e.key === "Enter") { /* procesar */ }
    if (e.ctrlKey && e.key === "s") {
        e.preventDefault();    // evita que el navegador guarde la página
        guardar();
    }
});

document.addEventListener("keyup",   fn);  // al soltar la tecla
document.addEventListener("keypress", fn);  // deprecado — usá keydown

Formularios

// Input — se dispara en cada cambio de carácter
input.addEventListener("input",   fn);

// Change — se dispara al perder el foco con un valor diferente
input.addEventListener("change",  fn);

// Focus y blur
input.addEventListener("focus",   fn);  // al entrar en el campo
input.addEventListener("blur",    fn);  // al salir del campo

// Formulario
form.addEventListener("submit", (e) => {
    e.preventDefault();   // evita que la página se recargue
    // procesar datos...
});
Probá el Ejemplo 2: Eventos de formulario

El objeto event

Cuando ocurre un evento, JavaScript pasa automáticamente un objeto event a la función manejadora. Contiene información sobre lo que pasó.

document.addEventListener("click", (event) => {
    // Información del evento
    event.type;           // "click"
    event.target;         // el elemento que recibió el clic
    event.currentTarget;  // el elemento donde está el listener

    // Posición del mouse
    event.clientX;        // posición X relativa a la ventana
    event.clientY;        // posición Y relativa a la ventana
    event.pageX;          // posición X relativa al documento

    // Control del evento
    event.preventDefault();   // cancela el comportamiento por defecto
    event.stopPropagation();  // detiene la propagación hacia arriba
});

// Ejemplo práctico — saber en qué elemento se hizo clic
document.addEventListener("click", (e) => {
    console.log("Elemento:", e.target.tagName);
    console.log("ID:",       e.target.id);
    console.log("Clases:",   e.target.className);
});

Delegación de eventos

En vez de poner un listener en cada elemento hijo, ponés uno solo en el padre y usás event.target para saber cuál fue el origen. Es más eficiente y funciona incluso con elementos creados dinámicamente.

// Sin delegación — un listener por cada botón (ineficiente)
document.querySelectorAll(".btn-borrar").forEach(btn => {
    btn.addEventListener("click", () => btn.parentElement.remove());
});

// Con delegación — un solo listener en el padre (eficiente)
document.getElementById("lista").addEventListener("click", (e) => {
    // e.target es el elemento que recibió el clic
    if (e.target.classList.contains("btn-borrar")) {
        e.target.closest("li").remove();
    }
    if (e.target.classList.contains("btn-completar")) {
        e.target.closest("li").classList.toggle("completada");
    }
});
closest() — subir por el árbol DOM

e.target.closest("li") sube por los ancestros del elemento y devuelve el primer <li> que encuentre. Es ideal para encontrar el contenedor cuando el clic ocurre en un hijo.

Probá el Ejemplo 3: Delegación de eventos

Remover listeners y una sola vez

// Guardar referencia a la función para poder removerla después
function manejarClic() {
    console.log("clic");
}

boton.addEventListener("click", manejarClic);
// ... más tarde:
boton.removeEventListener("click", manejarClic);

// Ejecutar el listener una sola vez con { once: true }
boton.addEventListener("click", () => {
    console.log("Solo se ejecuta la primera vez");
}, { once: true });

// AbortController — remover múltiples listeners a la vez
const controller = new AbortController();
const signal     = controller.signal;

boton.addEventListener("click",   fn1, { signal });
input.addEventListener("input",   fn2, { signal });
form.addEventListener("submit",   fn3, { signal });

// Remover todos de una vez
controller.abort();

Ejemplo Completo — Calculadora interactiva

Una calculadora que combina eventos de clic, teclado y formulario:

const inputA     = document.getElementById("numA");
const inputB     = document.getElementById("numB");
const resultado  = document.getElementById("resultado");

function calcular(operacion) {
    const a = parseFloat(inputA.value) || 0;
    const b = parseFloat(inputB.value) || 0;

    const ops = {
        suma:   a + b,
        resta:  a - b,
        mult:   a * b,
        div:    b !== 0 ? a / b : "Error: división por cero",
    };

    const res = ops[operacion] ?? "Operación inválida";
    resultado.textContent = typeof res === "number"
        ? res.toFixed(4).replace(/\.?0+$/, "")
        : res;
}

// Clic en cada botón de operación
document.querySelectorAll("[data-op]").forEach(btn => {
    btn.addEventListener("click", () => calcular(btn.dataset.op));
});

// Teclado — shortcuts
document.addEventListener("keydown", (e) => {
    if (e.key === "+") calcular("suma");
    if (e.key === "-") calcular("resta");
    if (e.key === "*") calcular("mult");
    if (e.key === "/") { e.preventDefault(); calcular("div"); }
});
Probá el Ejemplo 4: Calculadora interactiva

Buenas Prácticas

  • Siempre addEventListener: evitá onclick en HTML y como propiedad
  • preventDefault() en formularios: siempre que procesés el submit con JS, evitá el recargado de página
  • Delegación para listas dinámicas: si agregás elementos al DOM después de cargar, el listener en el padre los captura automáticamente
  • { once: true } para eventos únicos: más limpio que remover manualmente el listener
  • Guardá referencias si vas a remover: removeEventListener necesita la misma referencia de función — las arrow functions anónimas no se pueden remover
  • No abuses de listeners en document: ponerlos en el elemento más específico posible es más eficiente