L69 - Eventos
🧠 Concepto
Section titled “🧠 Concepto”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 listenerboton.addEventListener('click', function(e) { console.log('Botón clickeado');});
// Con arrow functionboton.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
removeEventListenerla función debe ser nombrada (no anónima). Con arrow functions anónimas no se puede eliminar.
📝 El objeto Event
Section titled “📝 El objeto Event”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étodos importantes del Event
Section titled “Métodos importantes del Event”| Método | Efecto |
|---|---|
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 defectoenlace.addEventListener('click', (e) => { e.preventDefault(); // No navega a la URL console.log('Enlace clickeado, pero no navegamos');});
// Detener propagaciónboton.addEventListener('click', (e) => { e.stopPropagation(); // El clic no llega al contenedor padre});🎯 Fases del evento: captura y burbujeo
Section titled “🎯 Fases del evento: captura y burbujeo”Cuando ocurre un evento en un elemento anidado, el evento viaja en tres fases:
- Fase de captura: desde
documenthacia abajo hasta el target (poco usada). - Fase de target: el evento llega al elemento que disparó el evento.
- 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 lidocument.querySelectorAll('.lista li').forEach(li => { li.addEventListener('click', () => { console.log('Item clickeado'); });});
// ✅ Eficiente: un solo listener en el padredocument.querySelector('.lista').addEventListener('click', (e) => { const item = e.target.closest('li'); if (item) { console.log('Item clickeado:', item.textContent); }});Ventajas de la delegación
Section titled “Ventajas de la delegación”- Rendimiento: un solo listener en lugar de N.
- Dinamismo: funciona para elementos que se añaden después de registrar el listener.
- Menos memoria: menos closures en memoria.
// Los elementos añadidos después también funcionanconst lista = document.querySelector('.lista');lista.addEventListener('click', (e) => { if (e.target.matches('li')) { e.target.classList.toggle('seleccionado'); }});
// Nuevo elemento añadido dinámicamenteconst nuevo = document.createElement('li');nuevo.textContent = 'Elemento nuevo';lista.appendChild(nuevo);// ¡Sigue funcionando sin añadir otro listener!📝 Eventos comunes
Section titled “📝 Eventos comunes”boton.addEventListener('click', () => { console.log('Clic simple');});boton.addEventListener('dblclick', () => { console.log('Doble clic');});submit (formularios)
Section titled “submit (formularios)”formulario.addEventListener('submit', (e) => { e.preventDefault(); // Evita recargar la página const datos = new FormData(formulario); console.log(Object.fromEntries(datos));});keydown / keyup
Section titled “keydown / keyup”document.addEventListener('keydown', (e) => { console.log(`Tecla: ${e.key}, Código: ${e.code}`); if (e.key === 'Enter' && e.ctrlKey) { console.log('Ctrl + Enter presionado'); }});DOMContentLoaded vs load
Section titled “DOMContentLoaded vs load”// 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 cargadowindow.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 futurasgaleria.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.