3.2 – EVENTOS EN JAVASCRIPT

1. Qué es un evento

Un evento es cualquier interacción que ocurre en la página:

  • El usuario hace clic
  • Escribe en un input
  • Envía un formulario
  • Mueve el ratón
  • Pulsa una tecla
  • La página termina de cargar

Cuando ocurre algo → el navegador lo detecta → JavaScript puede reaccionar.

El DOM no es solo un árbol… es un sistema reactivo.


2. Qué es un Event Listener

Un Event Listener es una función que espera a que ocurra un evento.

Cuando el evento ocurre → se ejecuta la función.

La forma moderna y correcta de escuchar eventos es:

elemento.addEventListener("evento", funcion);

3. Sintaxis de addEventListener

boton.addEventListener("click", () => {
  console.log("Se hizo clic");
});

Parámetros:

  • Tipo de evento → "click"
  • Función a ejecutar → callback

4. Ejemplo básico

HTML:

<buttonid="btn">Haz clic</button>

JavaScript:

const btn = document.getElementById("btn");

btn.addEventListener("click", () => {
  alert("Botón pulsado");
});

5. El objeto event

Cada vez que ocurre un evento, JavaScript genera un objeto llamado event que contiene información.

btn.addEventListener("click", (event) => {
  console.log(event);
});

Propiedades útiles:

PropiedadQué indica
event.targetElemento que disparó el evento
event.typeTipo de evento
event.clientXPosición X del ratón
event.clientYPosición Y del ratón
event.keyTecla pulsada
event.preventDefault()Evita comportamiento por defecto
event.stopPropagation()Detiene propagación

6. Eventos más comunes

Click

element.addEventListener("click", fn);

Input (cuando el usuario escribe)

input.addEventListener("input", fn);

Change (cuando cambia valor)

select.addEventListener("change", fn);

Submit (formularios)

form.addEventListener("submit", fn);

Keydown (teclado)

document.addEventListener("keydown", fn);

Mouseover

element.addEventListener("mouseover", fn);

7. Evitar comportamiento por defecto

Ejemplo: evitar que un formulario recargue la página.

form.addEventListener("submit", (e) => {
  e.preventDefault();
  console.log("Formulario capturado");
});

8. Propagación de eventos (Bubbling)

Cuando ocurre un evento, no solo afecta al elemento… sube por el árbol DOM.

Ejemplo:

<div id="padre">
  <button id="hijo">Click</button>
</div>
padre.addEventListener("click", () => console.log("Padre"));
hijo.addEventListener("click", () => console.log("Hijo"));

Resultado al hacer click:

Hijo
Padre

Primero el elemento → luego sus padres.


9. Detener propagación

hijo.addEventListener("click", (e) => {
  e.stopPropagation();
});

10. Delegación de eventos (muy importante)

En lugar de añadir eventos a cada elemento, se añade uno solo al contenedor.

tabla.addEventListener("click", (e) => {
  if (e.target.classList.contains("btn-ver")) {
    const id = e.target.dataset.id;
    console.log("Ver registro", id);
  }
});

Ventajas:

  • Funciona con elementos creados dinámicamente
  • Mejor rendimiento
  • Menos código

Esto es fundamental cuando el DOM se genera con JavaScript.


11. Eliminar eventos

function clickHandler() {
  console.log("Click");
}

btn.addEventListener("click", clickHandler);
btn.removeEventListener("click", clickHandler);

La función debe ser la misma referencia.


12. Eventos en elementos creados dinámicamente

Cuando creas un elemento con JavaScript, debes añadir el listener manualmente.

const boton = document.createElement("button");
boton.textContent = "Eliminar";

boton.addEventListener("click", () => {
  console.log("Elemento eliminado");
});

Tabla de eventos en JavaScript

🖱️ Eventos del ratón

EventoCuándo ocurreUso típico
clickClic normalBotones, enlaces
dblclickDoble clicAcciones especiales
mousedownPulsar botón ratónDrag, control
mouseupSoltar botónFinalizar acción
mousemoveMover ratónTracking, dibujo
mouseoverEntrar en elementoHover
mouseoutSalir de elementoQuitar hover
mouseenterEntrar (sin bubbling)UI precisa
mouseleaveSalir (sin bubbling)UI precisa
contextmenuClic derechoMenú contextual

⌨️ Eventos del teclado

EventoCuándo ocurreUso típico
keydownTecla presionadaDetectar tecla
keyupTecla liberadaConfirmar entrada
keypressTecla mantenida (deprecated)Evitar usar

Propiedad útil: event.key


📝 Eventos de formularios / inputs

EventoCuándo ocurreUso típico
inputCambia el valor en tiempo realValidación viva
changeValor confirmadoSelect, checkbox
submitEnvío de formularioCapturar datos
resetReset formularioRestaurar
focusElemento recibe focoUX
blurElemento pierde focoValidación
invalidValidación HTML fallaFormularios

📄 Eventos del documento / ventana

EventoCuándo ocurreUso típico
DOMContentLoadedDOM listoInicializar app
loadPágina completamente cargadaRecursos
resizeCambia tamaño ventanaResponsive
scrollScroll de páginaLazy load
beforeunloadAntes de salirConfirmación
unloadPágina se cierraLimpieza

🎯 Eventos de interacción avanzada

EventoCuándo ocurreUso típico
dragstartInicio dragDrag & Drop
dragoverArrastrando encimaDrop zones
dropSoltar elementoUpload
touchstartTocar pantallaMóvil
touchmoveDeslizarGestos
touchendFin toqueMobile UI
pointerdownInput universalMultiplataforma

📡 Eventos de red y estado

EventoCuándo ocurreUso típico
onlineVuelve conexiónReintentar
offlineSin conexiónAvisar
errorError recursoDebug
abortCancelaciónFetch

🎬 Eventos multimedia

EventoCuándo ocurreUso típico
playReproducirVideo/audio
pausePausarControl
endedFinaliza mediaLoop
timeupdateTiempo cambiaBarra progreso
volumechangeVolumen cambiaUI

🧠 Propiedades comunes del objeto event

PropiedadQué indica
event.targetElemento que disparó el evento
event.currentTargetElemento con listener
event.typeTipo de evento
event.keyTecla
event.clientX/YPosición ratón
event.preventDefault()Evita comportamiento
event.stopPropagation()Detiene bubbling

Ejemplo guiado: cargar JSON por fetch al inicio y pintarlo en el DOM

  1. Al abrir la página, se ejecuta código al evento DOMContentLoaded
  2. Se llama a un endpoint con fetch()
  3. Se convierte la respuesta a JSON
  4. Se renderiza una tabla en el DOM
  5. Se manejan estados: cargando / error / sin datos

1) HTML (estructura base)

Crea un archivo index.html:

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>JSON → DOM</title>
  <style>
    body { font-family: system-ui, Arial; padding: 20px; }
    .status { padding: 10px; border-radius: 8px; margin: 10px 0; }
    .loading { background: #fff3cd; }
    .error { background: #f8d7da; }
    .ok { background: #d1e7dd; }
    table { border-collapse: collapse; width: 100%; margin-top: 10px; }
    th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
    th { background: #f3f3f3; }
  </style>
</head>

<body>
  <h1>Listado de coches</h1>

  <div id="status" class="status loading">Cargando...</div>

  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>Marca</th>
        <th>Modelo</th>
        <th>Precio</th>
      </tr>
    </thead>
    <tbody id="rows"></tbody>
  </table>

  <script src="app.js"></script>
</body>
</html>

Qué estamos preparando aquí

  • Un div#status para mostrar mensajes (cargando/error/vacío).
  • Un tbody#rows donde vamos a insertar filas dinámicamente.
  • Cargamos app.js al final.

2) JavaScript (cargar y pintar)

Crea un archivo app.js:

// 1) Este evento se dispara cuando el DOM ya está construido.
//    (Importante: aún puede que imágenes o recursos no hayan cargado, pero el HTML sí.)
document.addEventListener("DOMContentLoaded", () => {
  loadCars();
});

// 2) Función principal: obtiene datos y renderiza
async function loadCars() {
  try {
    setStatus("Cargando...", "loading");

    // Cambia esta URL por tu endpoint real (por ejemplo en JEE: /miApp/api/coches)
    const url = "coches.json"; // Para pruebas: archivo local con JSON
    const response = await fetch(url);

    // 3) Comprobamos si la respuesta HTTP es correcta
    if (!response.ok) {
      throw new Error(`HTTP ${response.status} - ${response.statusText}`);
    }

    // 4) Convertimos el body a JSON (array de objetos)
    const cars = await response.json();

    // 5) Si no hay datos, mostramos estado vacío
    if (!Array.isArray(cars) || cars.length === 0) {
      clearRows();
      setStatus("No hay datos disponibles.", "ok");
      return;
    }

    // 6) Pintamos los datos
    renderCars(cars);
    setStatus(`Cargados ${cars.length} registros.`, "ok");

  } catch (error) {
    clearRows();
    setStatus(`Error cargando datos: ${error.message}`, "error");
  }
}

// --- Renderizado ---

function renderCars(cars) {
  const tbody = document.getElementById("rows");
  tbody.innerHTML = ""; // limpiamos antes de pintar

  // Creamos un fragmento para insertar todo de golpe (más eficiente)
  const fragment = document.createDocumentFragment();

  cars.forEach(car => {
    fragment.append(createRow(car));
  });

  tbody.append(fragment);
}

function createRow(car) {
  const tr = document.createElement("tr");

  const tdId = document.createElement("td");
  tdId.textContent = car.id;

  const tdMarca = document.createElement("td");
  tdMarca.textContent = car.marca;

  const tdModelo = document.createElement("td");
  tdModelo.textContent = car.modelo;

  const tdPrecio = document.createElement("td");
  tdPrecio.textContent = car.precio;

  tr.append(tdId, tdMarca, tdModelo, tdPrecio);
  return tr;
}

function clearRows() {
  document.getElementById("rows").innerHTML = "";
}

// --- Estado UI (mensajes) ---

function setStatus(message, type) {
  const status = document.getElementById("status");
  status.textContent = message;

  // Reseteamos clases y aplicamos la nueva
  status.classList.remove("loading", "error", "ok");
  status.classList.add(type);
}

Qué has aprendido aquí

  • DOMContentLoaded asegura que el HTML existe antes de seleccionar nodos.
  • fetch() hace la petición.
  • response.ok comprueba si el HTTP fue bien.
  • response.json() parsea la respuesta.
  • renderCars() separa “pintar” de “cargar datos”.
  • setStatus() maneja estados.

3) JSON de prueba (para que funcione sin backend)

Crea coches.json en la misma carpeta:

[
  { "id": 1, "marca": "Toyota", "modelo": "Corolla", "precio": 20000 },
  { "id": 2, "marca": "Ford", "modelo": "Focus", "precio": 18000 },
  { "id": 3, "marca": "Seat", "modelo": "Ibiza", "precio": 15500 }
]

4) Nota importante (muy típica en clase)

Si abres index.html haciendo doble clic (ruta file://), en algunos navegadores el fetch("coches.json") puede fallar por permisos.

Soluciones sencillas para clase:

  • Servirlo desde un servidor local (ideal).
  • Si ya estás con JEE, sirve la página desde tu propio proyecto y el endpoint desde el servlet.

5) Conectarlo a JEE

Cuando tengas tu endpoint en JEE, normalmente será algo así:

  • URL: /miApp/api/coches
  • Devuelve JSON

Solo cambias:

consturl="/miApp/api/coches";

Y listo.