¡Ahora viene la magia! Los eventos son las acciones que puede realizar el usuario (o el navegador) como hacer click, escribir, mover el mouse, etc. Los Event Listeners son las "orejas" que ponemos en JavaScript para escuchar estos eventos y reaccionar a ellos.
Un evento es algo que sucede en la página web:
Los eventos son la base de la interactividad en las páginas web.
addEventListener()addEventListener() es el método moderno y recomendado para escuchar eventos:
elemento.addEventListener('tipoDeEvento', funcionQueSeEjecuta)
<button id="mi-boton">¡Haz click!</button>
const boton = document.querySelector('#mi-boton')
// Escuchar el evento 'click'
boton.addEventListener('click', function() {
console.log('¡Botón clickeado!')
})
// Con arrow function (más común)
boton.addEventListener('click', () => {
console.log('¡Botón clickeado!')
})
click - Click del mouseconst boton = document.querySelector('#boton')
boton.addEventListener('click', () => {
console.log('Click en el botón')
})
dblclick - Doble clickconst imagen = document.querySelector('#imagen')
imagen.addEventListener('dblclick', () => {
console.log('Doble click en la imagen')
})
mouseenter y mouseleave - Entrar y salir con el mouseconst caja = document.querySelector('#caja')
caja.addEventListener('mouseenter', () => {
caja.style.backgroundColor = 'lightblue'
console.log('Mouse entró en la caja')
})
caja.addEventListener('mouseleave', () => {
caja.style.backgroundColor = 'white'
console.log('Mouse salió de la caja')
})
keydown - Cuando se presiona una teclaconst input = document.querySelector('#mi-input')
input.addEventListener('keydown', (evento) => {
console.log('Tecla presionada:', evento.key)
if (evento.key === 'Enter') {
console.log('Se presionó Enter')
}
})
keyup - Cuando se suelta una teclainput.addEventListener('keyup', (evento) => {
console.log('Tecla soltada:', evento.key)
console.log('Valor actual del input:', input.value)
})
submit - Envío de formularioconst formulario = document.querySelector('#mi-formulario')
formulario.addEventListener('submit', (evento) => {
evento.preventDefault() // Evita que se recargue la página
console.log('Formulario enviado')
// Aquí puedes procesar los datos del formulario
})
input - Cambio en input (en tiempo real)const input = document.querySelector('#busqueda')
input.addEventListener('input', () => {
console.log('Usuario escribiendo:', input.value)
// Ideal para búsquedas en tiempo real
})
change - Cambio en input (cuando pierde el foco)const select = document.querySelector('#pais')
select.addEventListener('change', () => {
console.log('País seleccionado:', select.value)
})
load - Cuando la página termina de cargarwindow.addEventListener('load', () => {
console.log('Página completamente cargada')
})
resize - Cuando se redimensiona la ventanawindow.addEventListener('resize', () => {
console.log('Ventana redimensionada')
console.log('Nuevo ancho:', window.innerWidth)
console.log('Nueva altura:', window.innerHeight)
})
Cuando ocurre un evento, JavaScript crea un objeto Event con información útil:
boton.addEventListener('click', (evento) => {
console.log('Objeto evento:', evento)
console.log('Tipo de evento:', evento.type) // 'click'
console.log('Elemento que disparó:', evento.target) // El botón
console.log('Posición X:', evento.clientX) // Posición del click
console.log('Posición Y:', evento.clientY)
})
input.addEventListener('keydown', (evento) => {
console.log('Tecla:', evento.key) // Tecla presionada
console.log('Código:', evento.code) // Código físico de la tecla
console.log('Ctrl presionado:', evento.ctrlKey) // true/false
console.log('Shift presionado:', evento.shiftKey) // true/false
console.log('Alt presionado:', evento.altKey) // true/false
})
preventDefault() - Prevenir comportamiento por defectoconst enlace = document.querySelector('#mi-enlace')
enlace.addEventListener('click', (evento) => {
evento.preventDefault() // Evita que el enlace navegue
console.log('Click en enlace, pero no navega')
})
const formulario = document.querySelector('#formulario')
formulario.addEventListener('submit', (evento) => {
evento.preventDefault() // Evita que se recargue la página
console.log('Formulario procesado sin recargar')
})
<div id="contador-app">
<h2>Contador: <span id="numero">0</span></h2>
<div class="botones">
<button id="decrementar">-1</button>
<button id="reset">Reset</button>
<button id="incrementar">+1</button>
</div>
<div class="controles">
<input type="number" id="paso" value="1" min="1">
<label for="paso">Paso</label>
</div>
</div>
// Estado
let contador = 0
// Seleccionar elementos
const numeroElement = document.querySelector('#numero')
const btnIncrementar = document.querySelector('#incrementar')
const btnDecrementar = document.querySelector('#decrementar')
const btnReset = document.querySelector('#reset')
const inputPaso = document.querySelector('#paso')
// Función para actualizar la UI
function actualizarContador() {
numeroElement.textContent = contador
// Cambiar color según el valor
numeroElement.className = '' // Limpiar clases
if (contador > 0) {
numeroElement.classList.add('positivo')
} else if (contador < 0) {
numeroElement.classList.add('negativo')
}
}
// Event listeners
btnIncrementar.addEventListener('click', () => {
const paso = parseInt(inputPaso.value) || 1
contador += paso
actualizarContador()
})
btnDecrementar.addEventListener('click', () => {
const paso = parseInt(inputPaso.value) || 1
contador -= paso
actualizarContador()
})
btnReset.addEventListener('click', () => {
contador = 0
actualizarContador()
})
// Atajos de teclado
document.addEventListener('keydown', (evento) => {
if (evento.key === 'ArrowUp') {
evento.preventDefault()
contador++
actualizarContador()
} else if (evento.key === 'ArrowDown') {
evento.preventDefault()
contador--
actualizarContador()
} else if (evento.key === 'r' || evento.key === 'R') {
contador = 0
actualizarContador()
}
})
<form id="formulario-registro">
<div class="campo">
<label for="email">Email:</label>
<input type="email" id="email" required>
<span class="error" id="error-email"></span>
</div>
<div class="campo">
<label for="password">Contraseña:</label>
<input type="password" id="password" required>
<span class="error" id="error-password"></span>
</div>
<div class="campo">
<label for="confirmar-password">Confirmar Contraseña:</label>
<input type="password" id="confirmar-password" required>
<span class="error" id="error-confirmar"></span>
</div>
<button type="submit" id="btn-enviar" disabled>Registrarse</button>
</form>
// Seleccionar elementos
const formulario = document.querySelector('#formulario-registro')
const emailInput = document.querySelector('#email')
const passwordInput = document.querySelector('#password')
const confirmarInput = document.querySelector('#confirmar-password')
const btnEnviar = document.querySelector('#btn-enviar')
// Elementos de error
const errorEmail = document.querySelector('#error-email')
const errorPassword = document.querySelector('#error-password')
const errorConfirmar = document.querySelector('#error-confirmar')
// Estado de validación
let validacion = {
email: false,
password: false,
confirmar: false
}
// Función para actualizar el botón de envío
function actualizarBotonEnvio() {
const todoValido = Object.values(validacion).every(valido => valido)
btnEnviar.disabled = !todoValido
}
// Validar email
emailInput.addEventListener('input', () => {
const email = emailInput.value
const esValido = email.includes('@') && email.includes('.')
if (email.length === 0) {
errorEmail.textContent = ''
validacion.email = false
} else if (esValido) {
errorEmail.textContent = ''
errorEmail.style.color = 'green'
errorEmail.textContent = '✓ Email válido'
validacion.email = true
} else {
errorEmail.style.color = 'red'
errorEmail.textContent = '✗ Email no válido'
validacion.email = false
}
actualizarBotonEnvio()
})
// Validar contraseña
passwordInput.addEventListener('input', () => {
const password = passwordInput.value
const esValida = password.length >= 6
if (password.length === 0) {
errorPassword.textContent = ''
validacion.password = false
} else if (esValida) {
errorPassword.style.color = 'green'
errorPassword.textContent = '✓ Contraseña válida'
validacion.password = true
} else {
errorPassword.style.color = 'red'
errorPassword.textContent = '✗ Mínimo 6 caracteres'
validacion.password = false
}
// Re-validar confirmación si ya tiene contenido
if (confirmarInput.value.length > 0) {
confirmarInput.dispatchEvent(new Event('input'))
}
actualizarBotonEnvio()
})
// Validar confirmación de contraseña
confirmarInput.addEventListener('input', () => {
const confirmar = confirmarInput.value
const password = passwordInput.value
const coinciden = confirmar === password && confirmar.length > 0
if (confirmar.length === 0) {
errorConfirmar.textContent = ''
validacion.confirmar = false
} else if (coinciden) {
errorConfirmar.style.color = 'green'
errorConfirmar.textContent = '✓ Contraseñas coinciden'
validacion.confirmar = true
} else {
errorConfirmar.style.color = 'red'
errorConfirmar.textContent = '✗ Las contraseñas no coinciden'
validacion.confirmar = false
}
actualizarBotonEnvio()
})
// Manejar envío del formulario
formulario.addEventListener('submit', (evento) => {
evento.preventDefault()
console.log('Formulario válido enviado:')
console.log('Email:', emailInput.value)
console.log('Password:', passwordInput.value)
// Aquí harías la petición al servidor
alert('¡Registro exitoso!')
})
<div id="galeria">
<div class="imagen-principal">
<img id="imagen-grande" src="imagen1.jpg" alt="Imagen principal">
</div>
<div class="miniaturas">
<img src="imagen1.jpg" alt="Imagen 1" class="miniatura activa" data-src="imagen1.jpg">
<img src="imagen2.jpg" alt="Imagen 2" class="miniatura" data-src="imagen2.jpg">
<img src="imagen3.jpg" alt="Imagen 3" class="miniatura" data-src="imagen3.jpg">
<img src="imagen4.jpg" alt="Imagen 4" class="miniatura" data-src="imagen4.jpg">
</div>
<div class="controles">
<button id="anterior">← Anterior</button>
<button id="siguiente">Siguiente →</button>
</div>
</div>
// Seleccionar elementos
const imagenGrande = document.querySelector('#imagen-grande')
const miniaturas = document.querySelectorAll('.miniatura')
const btnAnterior = document.querySelector('#anterior')
const btnSiguiente = document.querySelector('#siguiente')
let imagenActual = 0
// Función para cambiar imagen
function cambiarImagen(indice) {
// Actualizar imagen grande
imagenGrande.src = miniaturas[indice].dataset.src
imagenGrande.alt = miniaturas[indice].alt
// Actualizar clases activas
miniaturas.forEach(mini => mini.classList.remove('activa'))
miniaturas[indice].classList.add('activa')
// Actualizar índice actual
imagenActual = indice
}
// Event listeners para miniaturas
miniaturas.forEach((miniatura, indice) => {
miniatura.addEventListener('click', () => {
cambiarImagen(indice)
})
// Efecto hover
miniatura.addEventListener('mouseenter', () => {
miniatura.style.opacity = '0.7'
})
miniatura.addEventListener('mouseleave', () => {
miniatura.style.opacity = '1'
})
})
// Botones de navegación
btnAnterior.addEventListener('click', () => {
const nuevoIndice = imagenActual === 0 ? miniaturas.length - 1 : imagenActual - 1
cambiarImagen(nuevoIndice)
})
btnSiguiente.addEventListener('click', () => {
const nuevoIndice = imagenActual === miniaturas.length - 1 ? 0 : imagenActual + 1
cambiarImagen(nuevoIndice)
})
// Navegación con teclado
document.addEventListener('keydown', (evento) => {
if (evento.key === 'ArrowLeft') {
btnAnterior.click()
} else if (evento.key === 'ArrowRight') {
btnSiguiente.click()
}
})
Cuando tienes muchos elementos similares, en lugar de añadir un event listener a cada uno, puedes usar delegación:
<ul id="lista-tareas">
<li>Tarea 1 <button class="eliminar">X</button></li>
<li>Tarea 2 <button class="eliminar">X</button></li>
<li>Tarea 3 <button class="eliminar">X</button></li>
<!-- Se pueden añadir más dinámicamente -->
</ul>
const lista = document.querySelector('#lista-tareas')
// Un solo event listener en el contenedor padre
lista.addEventListener('click', (evento) => {
// Verificar si se clickeó un botón de eliminar
if (evento.target.classList.contains('eliminar')) {
const tarea = evento.target.parentElement
tarea.remove()
}
})
// Añadir nuevas tareas dinámicamente
function agregarTarea(texto) {
const li = document.createElement('li')
li.innerHTML = `${texto} <button class="eliminar">X</button>`
lista.appendChild(li)
// ¡El event listener ya funciona automáticamente!
}
function manejarClick() {
console.log('Click manejado')
}
const boton = document.querySelector('#boton')
// Añadir
boton.addEventListener('click', manejarClick)
// Remover (debe ser la misma función)
boton.removeEventListener('click', manejarClick)
// Usar addEventListener (método moderno)
boton.addEventListener('click', manejarClick)
// Usar arrow functions para funciones simples
boton.addEventListener('click', () => {
console.log('Click simple')
})
// Usar preventDefault cuando sea necesario
formulario.addEventListener('submit', (evento) => {
evento.preventDefault()
// Procesar formulario
})
// Delegación para elementos dinámicos
contenedor.addEventListener('click', (evento) => {
if (evento.target.matches('.boton-dinamico')) {
// Manejar click
}
})
// No usar onclick (método antiguo)
boton.onclick = manejarClick // ❌ Solo permite un listener
// No crear funciones anónimas si necesitas removerlas
boton.addEventListener('click', function() {
console.log('No se puede remover')
}) // ❌ No se puede remover después
// No añadir muchos listeners iguales
elementos.forEach(elemento => {
elemento.addEventListener('click', mismaFuncion) // ❌ Ineficiente
})
addEventListener(): Método moderno para escuchar eventospreventDefault(): Previene el comportamiento por defectoEn la próxima lección pondremos todo junto en un proyecto práctico completo.
Inicia sesión para guardar tu progreso