Skip to content

L69 - Eventos

Los eventos son acciones u ocurrencias que suceden en el sistema (clic del usuario, pulsación de tecla, carga de página, envío de formulario…) y que JavaScript puede escuchar y responder a ellas.

💻 addEventListener() y removeEventListener()

Section titled “💻 addEventListener() y removeEventListener()”
const boton = document.querySelector('#miBoton');
// Añadir un event listener
boton.addEventListener('click', function(e) {
console.log('Botón clickeado');
});
// Con arrow function
boton.addEventListener('click', (e) => {
console.log('Botón clickeado');
});
// Eliminar un event listener (debe ser la misma referencia)
function handleClick(e) {
console.log('Clic');
}
boton.addEventListener('click', handleClick);
// Más tarde...
boton.removeEventListener('click', handleClick);

⚠️ Para removeEventListener la función debe ser nombrada (no anónima). Con arrow functions anónimas no se puede eliminar.

Cuando se dispara un evento, el callback recibe un objeto Event con información útil:

elemento.addEventListener('click', (event) => {
console.log(event.type); // 'click'
console.log(event.target); // el elemento que disparó el evento
console.log(event.currentTarget); // el elemento donde está el listener
console.log(event.clientX); // coordenada X del clic
console.log(event.clientY); // coordenada Y del clic
console.log(event.timeStamp); // momento del evento (ms desde carga)
});
MétodoEfecto
preventDefault()Cancela el comportamiento por defecto (ej: seguir un enlace)
stopPropagation()Detiene la propagación del evento (no llega a padres)
stopImmediatePropagation()Detiene la propagación y demás listeners del mismo elemento
// Prevenir comportamiento por defecto
enlace.addEventListener('click', (e) => {
e.preventDefault(); // No navega a la URL
console.log('Enlace clickeado, pero no navegamos');
});
// Detener propagación
boton.addEventListener('click', (e) => {
e.stopPropagation(); // El clic no llega al contenedor padre
});

Cuando ocurre un evento en un elemento anidado, el evento viaja en tres fases:

  1. Fase de captura: desde document hacia abajo hasta el target (poco usada).
  2. Fase de target: el evento llega al elemento que disparó el evento.
  3. Fase de burbujeo: desde el target hacia arriba hasta document (por defecto).
<div id="abuelo">
<div id="padre">
<div id="hijo">Clic aquí</div>
</div>
</div>
document.querySelectorAll('div').forEach(div => {
div.addEventListener('click', (e) => {
console.log(`Burbujeo: ${div.id}`);
});
// Tercer argumento true = fase de captura
div.addEventListener('click', (e) => {
console.log(`Captura: ${div.id}`);
}, true);
});
// Si haces clic en #hijo, el orden es:
// Captura: abuelo → padre → hijo
// Burbujeo: hijo → padre → abuelo

💻 Event Delegation (Delegación de eventos)

Section titled “💻 Event Delegation (Delegación de eventos)”

En lugar de asignar un listener a cada hijo, asignamos uno solo al padre y detectamos qué hijo recibió el evento mediante e.target:

// ❌ Ineficiente: un listener por cada li
document.querySelectorAll('.lista li').forEach(li => {
li.addEventListener('click', () => {
console.log('Item clickeado');
});
});
// ✅ Eficiente: un solo listener en el padre
document.querySelector('.lista').addEventListener('click', (e) => {
const item = e.target.closest('li');
if (item) {
console.log('Item clickeado:', item.textContent);
}
});
  1. Rendimiento: un solo listener en lugar de N.
  2. Dinamismo: funciona para elementos que se añaden después de registrar el listener.
  3. Menos memoria: menos closures en memoria.
// Los elementos añadidos después también funcionan
const lista = document.querySelector('.lista');
lista.addEventListener('click', (e) => {
if (e.target.matches('li')) {
e.target.classList.toggle('seleccionado');
}
});
// Nuevo elemento añadido dinámicamente
const nuevo = document.createElement('li');
nuevo.textContent = 'Elemento nuevo';
lista.appendChild(nuevo);
// ¡Sigue funcionando sin añadir otro listener!
boton.addEventListener('click', () => {
console.log('Clic simple');
});
boton.addEventListener('dblclick', () => {
console.log('Doble clic');
});
formulario.addEventListener('submit', (e) => {
e.preventDefault(); // Evita recargar la página
const datos = new FormData(formulario);
console.log(Object.fromEntries(datos));
});
document.addEventListener('keydown', (e) => {
console.log(`Tecla: ${e.key}, Código: ${e.code}`);
if (e.key === 'Enter' && e.ctrlKey) {
console.log('Ctrl + Enter presionado');
}
});
// Se dispara cuando el HTML está cargado y parseado (no espera imágenes, CSS)
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM listo, pero imágenes quizás no');
});
// Se dispara cuando todo (imágenes, scripts, CSS) ha cargado
window.addEventListener('load', () => {
console.log('Página completamente cargada');
});

🎯 Ejemplo práctico: galería con delegación

Section titled “🎯 Ejemplo práctico: galería con delegación”
const galeria = document.querySelector('.galeria');
// Un solo listener para todas las imágenes presentes y futuras
galeria.addEventListener('click', (e) => {
const img = e.target.closest('img');
if (!img) return;
const modal = document.createElement('div');
modal.className = 'modal';
modal.innerHTML = `<img src="${img.src}" alt="${img.alt}">
<button class="cerrar">✕</button>`;
modal.querySelector('.cerrar').addEventListener('click', () => {
modal.remove();
});
document.body.appendChild(modal);
});

🎯 Regla de oro: siempre que tengas listas o colecciones de elementos similares, usa delegación de eventos. Es más eficiente y funciona con elementos dinámicos.