Eventos del DOM en JavaScript: Guía completa para principiantes

Aprende a manejar eventos del DOM en JavaScript. Descubre cómo usar addEventListener
, tipos de eventos y ejemplos prácticos paso a paso.
¿Qué son los eventos del DOM en JavaScript?
Los eventos del DOM (Document Object Model) en JavaScript son acciones o sucesos que ocurren en el navegador y que pueden ser detectados y manejados mediante código. Estos eventos pueden ser generados por el usuario, como hacer clic en un botón, mover el ratón, presionar una tecla, o pueden ser eventos del sistema, como la carga de una página o la finalización de una solicitud de red.
Ciclo de vida de un evento: desde que ocurre hasta que se procesa
Cuando un evento ocurre en el DOM, sigue un ciclo de vida que incluye varias fases:
-
Captura: El evento se propaga desde el elemento raíz del DOM hacia el elemento objetivo. Durante esta fase, los manejadores de eventos registrados para la fase de captura se ejecutan.
-
Objetivo: El evento llega al elemento objetivo donde ocurrió la acción. Aquí, los manejadores de eventos registrados directamente en el elemento objetivo se ejecutan.
-
Burbuja: El evento se propaga de vuelta desde el elemento objetivo hacia el elemento raíz del DOM. Durante esta fase, los manejadores de eventos registrados para la fase de burbuja se ejecutan.
Diferencia entre eventos inline y addEventListener()
Eventos inline
Son aquellos que se definen directamente en el HTML utilizando atributos como onclick
, onmouseover
, etc. Por ejemplo:
<button onclick="alert('¡Hola!')">Haz clic aquí</button>
Ventajas:
- Fácil de implementar para eventos simples.
Desventajas:
- Mezcla de HTML y JavaScript, lo que puede dificultar el mantenimiento del código.
- No permite agregar múltiples manejadores de eventos al mismo elemento.
addEventListener()
Es un método de JavaScript que permite agregar manejadores de eventos a elementos del DOM de manera más flexible. Por ejemplo:
const button = document.querySelector("button");
button.addEventListener("click", () => {
alert("¡Hola!");
});
Ventajas:
-
Separa el HTML del JavaScript, mejorando la organización del código.
-
Permite agregar múltiples manejadores de eventos al mismo elemento.
Desventajas:
- Requiere un poco más de código para configurarlo.
Cómo usar addEventListener
para manejar eventos
Para usar addEventListener
, primero debes seleccionar el elemento del DOM al que deseas agregar el evento. Luego, llamas al método addEventListener
, pasando como argumentos el tipo de evento y la función que se ejecutará cuando ocurra el evento.
const button = document.querySelector("button");
button.addEventListener("click", () => {
alert("¡Hola!");
});
Tipos comunes de eventos en JavaScript
Eventos de mouse: click
, dblclick
, mouseover
, mouseout
, mousemove
const button = document.querySelector("button");
button.addEventListener("click", () => {
console.log("Botón clickeado");
});
button.addEventListener("dblclick", () => {
console.log("Botón doble clickeado");
});
button.addEventListener("mouseover", () => {
console.log("Mouse sobre el botón");
});
button.addEventListener("mouseout", () => {
console.log("Mouse fuera del botón");
});
button.addEventListener("mousemove", (event) => {
console.log(`Mouse moviéndose en: (${event.clientX}, ${event.clientY})`);
});
Eventos de teclado: keydown
, keyup
, keypress
const input = document.querySelector("input");
input.addEventListener("keydown", (event) => {
console.log(`Tecla presionada: ${event.key}`);
});
input.addEventListener("keyup", (event) => {
console.log(`Tecla liberada: ${event.key}`);
});
input.addEventListener("keypress", (event) => {
console.log(`Tecla presionada (keypress): ${event.key}`);
});
Eventos de formulario: submit
, change
, input
, focus
, blur
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault(); // Evita el envío del formulario
console.log("Formulario enviado");
});
const input = document.querySelector("input");
input.addEventListener("change", () => {
console.log("El valor del input ha cambiado");
});
input.addEventListener("input", () => {
console.log("El usuario está escribiendo");
});
input.addEventListener("focus", () => {
console.log("Input enfocado");
});
input.addEventListener("blur", () => {
console.log("Input desenfocado");
});
Eventos de ventana: load
, resize
, scroll
, unload
window.addEventListener("load", () => {
console.log("Página cargada");
});
window.addEventListener("resize", () => {
console.log("Ventana redimensionada");
});
window.addEventListener("scroll", () => {
console.log("Página desplazada");
});
window.addEventListener("unload", () => {
console.log("Página descargada");
});
Objeto event: qué es y cómo acceder a sus propiedades
Cuando un evento ocurre, se crea un objeto event
que contiene información sobre el evento, como el tipo de evento, el elemento objetivo, las coordenadas del mouse, la tecla presionada, entre otros. Puedes acceder a este objeto pasando un parámetro a la función del manejador de eventos.
button.addEventListener("click", (event) => {
console.log(`Tipo de evento: ${event.type}`);
console.log(`Elemento objetivo: ${event.target}`);
});
input.addEventListener("keydown", (event) => {
console.log(`Tecla presionada: ${event.key}`);
console.log(`Código de la tecla: ${event.code}`);
});
Uso de preventDefault()
para evitar el comportamiento por defecto
El método preventDefault()
se utiliza para evitar el comportamiento por defecto de un evento. Por ejemplo, en un formulario, el comportamiento por defecto al enviar es recargar la página. Puedes usar preventDefault()
para evitar esto y manejar el envío del formulario con JavaScript.
form.addEventListener("submit", (event) => {
event.preventDefault(); // Evita el envío del formulario
console.log("Formulario enviado sin recargar la página");
});
Uso de stopPropagation()
y propagación de eventos
La propagación de eventos es el proceso mediante el cual un evento se propaga a través del DOM, pasando por las fases de captura, objetivo y burbuja. A veces, es posible que desees evitar que un evento se propague a otros elementos. Para ello, puedes usar el método stopPropagation()
.
const parent = document.querySelector(".parent");
const child = document.querySelector(".child");
parent.addEventListener("click", () => {
console.log("Elemento padre clickeado");
});
child.addEventListener("click", (event) => {
event.stopPropagation(); // Evita que el evento se propague al padre
console.log("Elemento hijo clickeado");
});
Delegación de eventos: qué es y por qué conviene usarla
La delegación de eventos es una técnica que consiste en aprovechar la propagación de eventos para manejar eventos en elementos hijos a través de un único manejador de eventos en un elemento padre. Esto es especialmente útil cuando tienes muchos elementos similares (como una lista de ítems) y deseas manejar eventos sin tener que agregar un manejador a cada uno de ellos.
const list = document.querySelector("ul");
list.addEventListener("click", (event) => {
if (event.target && event.target.nodeName === "LI") {
console.log(`Ítem de la lista clickeado: ${event.target.textContent}`);
}
});
Diferencia entre eventos síncronos y asíncronos
-
Eventos síncronos: Son aquellos que se ejecutan inmediatamente cuando ocurren. Por ejemplo, un clic en un botón que ejecuta una función directamente.
-
Eventos asíncronos: Son aquellos que pueden ocurrir en un momento futuro y no bloquean la ejecución del código. Por ejemplo, una solicitud de red que se completa después de un tiempo y ejecuta una función cuando recibe la respuesta.
Uso de funciones anónimas en eventos
Puedes usar funciones anónimas (funciones sin nombre) directamente dentro del addEventListener
para manejar eventos. Esto es útil para eventos simples donde no necesitas reutilizar la función en otro lugar.
button.addEventListener("click", function () {
alert("¡Hola desde una función anónima!");
});
Remover eventos con removeEventListener()
Para remover un evento que ha sido agregado con addEventListener
, debes usar el método removeEventListener
. Es importante que la función que deseas remover sea una función nombrada o una referencia a una función, ya que no puedes remover funciones anónimas directamente.
function handleClick() {
alert("¡Hola!");
}
button.addEventListener("click", handleClick);
// Para remover el evento
button.removeEventListener("click", handleClick);
Eventos personalizados con CustomEvent
Puedes crear y disparar eventos personalizados utilizando la clase CustomEvent
. Esto es útil cuando deseas comunicarte entre diferentes partes de tu aplicación sin depender de eventos predefinidos.
const myEvent = new CustomEvent("miEvento", {
detail: { mensaje: "¡Hola desde un evento personalizado!" },
});
document.addEventListener("miEvento", (event) => {
console.log(event.detail.mensaje);
});
document.dispatchEvent(myEvent);
Eventos táctiles en dispositivos móviles: touchstart
y touchend
Cuando los usuarios interactúan con una pantalla táctil, no utilizan un mouse sino sus dedos. Para capturar estas acciones, JavaScript proporciona los eventos táctiles (touch events).
Principales eventos táctiles
-
touchstart: Se dispara cuando un dedo toca la pantalla. Es equivalente al evento mousedown en el mouse.
-
touchend: Ocurre cuando el dedo se levanta de la pantalla. Es equivalente a mouseup.
-
touchmove: Se ejecuta mientras el dedo se desplaza sobre la pantalla. Es equivalente a mousemove.
-
touchcancel: Se dispara cuando el sistema interrumpe la interacción táctil (por ejemplo, una llamada entrante o cambio de aplicación).
Ejemplo básico con touchstart
y touchend
<div
id="caja"
style="width:200px; height:200px; background:lightblue; text-align:center; line-height:200px;"
>
Tócame
</div>
<script>
const caja = document.getElementById("caja");
caja.addEventListener("touchstart", () => {
caja.style.background = "lightgreen";
caja.innerText = "¡Tocando!";
});
caja.addEventListener("touchend", () => {
caja.style.background = "lightblue";
caja.innerText = "Tócame";
});
</script>
En este ejemplo:
Cuando el usuario toca la caja, el fondo cambia a verde y el texto a “¡Tocando!”.
Cuando levanta el dedo, vuelve a su estado inicial.
Propiedades del objeto TouchEvent
Cuando trabajas con eventos táctiles, puedes acceder a información útil:
-
touches
: Lista de todos los puntos de contacto en la pantalla. -
targetTouches
: Puntos de contacto específicos sobre el elemento que disparó el evento. -
changedTouches
: Puntos de contacto que cambiaron en ese evento.
Ejemplo:
caja.addEventListener("touchstart", (event) => {
console.log("Número de dedos en pantalla:", event.touches.length);
});
Si el usuario toca con dos dedos, mostrará 2
Eventos de arrastrar y soltar: drag
y drop
El arrastrar y soltar (Drag & Drop) es una funcionalidad muy usada en interfaces modernas, como mover archivos, reorganizar elementos o crear editores visuales.
JavaScript y el DOM nos permiten trabajar con estos eventos de forma sencilla.
Principales eventos de arrastrar y soltar (Drag & Drop)
-
dragstart
: se dispara cuando un elemento comienza a arrastrarse. -
drag
: ocurre mientras el elemento se arrastra. -
dragend
: se ejecuta cuando el usuario deja de arrastrar. -
dragenter
: cuando un elemento arrastrado entra en un área válida de destino. -
dragover
: mientras el elemento se mueve dentro del área válida (se debe usarevent.preventDefault()
para permitir el drop). -
dragleave
: cuando el elemento arrastrado sale del área válida. -
drop
: ocurre cuando el usuario suelta el elemento sobre el área válida.
Ejemplo práctico: arrastrar y soltar un cuadro
<div
id="zona"
style="width:300px; height:200px; border:2px dashed gray; display:flex; align-items:center; justify-content:center; margin-bottom:20px;"
>
Arrastra aquí
</div>
<div
id="caja"
draggable="true"
style="width:100px; height:100px; background:lightblue; text-align:center; line-height:100px; cursor:grab;"
>
Caja
</div>
<script>
const zona = document.getElementById("zona");
const caja = document.getElementById("caja");
// Se inicia el arrastre
caja.addEventListener("dragstart", (event) => {
event.dataTransfer.setData("text", event.target.id);
caja.style.opacity = "0.5";
});
// Finaliza el arrastre
caja.addEventListener("dragend", () => {
caja.style.opacity = "1";
});
// Permitir soltar en la zona
zona.addEventListener("dragover", (event) => {
event.preventDefault();
zona.style.background = "#e0ffe0";
});
zona.addEventListener("dragleave", () => {
zona.style.background = "";
});
// Cuando se suelta el elemento
zona.addEventListener("drop", (event) => {
event.preventDefault();
const id = event.dataTransfer.getData("text");
const elemento = document.getElementById(id);
zona.appendChild(elemento);
zona.style.background = "";
});
</script>
Explicación del ejemplo:
El atributo draggable="true" hace que el elemento se pueda arrastrar.
Con event.dataTransfer.setData() guardamos información del elemento arrastrado.
El evento dragover debe tener event.preventDefault() para habilitar el drop.
En drop, obtenemos el elemento con getData y lo insertamos en la zona de destino.
Ahora tienes un cuadro azul que puedes arrastrar hacia la zona gris y soltarlo.
Cómo combinar eventos de teclado y mouse en una misma acción
En muchos casos necesitamos que el usuario realice una acción con el mouse pero que también dependa de una tecla del teclado.
Por ejemplo: hacer clic en un elemento solo si se mantiene presionada la tecla Shift
.
Esto se logra accediendo a las propiedades del objeto event
en los eventos del mouse.
Propiedades útiles del objeto event
event.shiftKey
: Indica si la teclaShift
está presionada.event.ctrlKey
: Indica si la teclaControl
está presionada.event.altKey
: Indica si la teclaAlt
está presionada.event.metaKey
: En Mac, indica si la tecla⌘ Command
está presionada.
Estas propiedades se pueden combinar con cualquier evento de mouse (click
, dblclick
, mousedown
, etc.).
Ejemplo práctico: clic + tecla Shift
<button id="btn">Haz clic aquí</button>
<script>
const btn = document.getElementById("btn");
btn.addEventListener("click", (event) => {
if (event.shiftKey) {
alert("¡Has hecho clic manteniendo presionada la tecla SHIFT!");
} else {
alert("Clic normal sin tecla SHIFT.");
}
});
</script>
En este ejemplo:
Si haces clic en el botón sin presionar ninguna tecla → muestra "Clic normal".
Si haces clic mientras mantienes presionada Shift → muestra "Has hecho clic con SHIFT".
Ejemplo avanzado: mover un objeto con teclado + clic
<div
id="caja"
style="width:100px; height:100px; background:lightblue; position:absolute; top:50px; left:50px; cursor:pointer;"
>
Caja
</div>
<script>
const caja = document.getElementById("caja");
document.addEventListener("keydown", (event) => {
// Guardamos si la tecla CTRL está presionada
if (event.key === "Control") {
caja.dataset.ctrl = "true";
caja.style.border = "2px dashed red";
}
});
document.addEventListener("keyup", (event) => {
if (event.key === "Control") {
caja.dataset.ctrl = "false";
caja.style.border = "none";
}
});
caja.addEventListener("click", (event) => {
if (caja.dataset.ctrl === "true") {
// Si se hace clic con CTRL presionado → mover la caja
caja.style.left = parseInt(caja.style.left) + 50 + "px";
} else {
alert(
"Haz clic mientras mantienes presionada la tecla CTRL para mover la caja."
);
}
});
</script>
Aquí combinamos teclado y mouse:
Al presionar Control
, la caja muestra un borde rojo.
Si haces clic en la caja mientras mantienes Control, la caja se mueve 50px hacia la derecha.
Si clickeas sin Control
, aparece un aviso.
Errores comunes al trabajar con eventos del DOM y cómo evitarlos
Trabajar con eventos en JavaScript puede parecer sencillo al inicio, pero es común cometer errores que dificultan el funcionamiento de una aplicación. A continuación veremos los errores más frecuentes al trabajar con eventos del DOM y las mejores prácticas para evitarlos.
1. Usar onclick
en lugar de addEventListener
Un error común es asignar eventos directamente con atributos HTML (onclick="miFuncion()"
). Esto limita la flexibilidad y puede sobrescribir otros eventos.
<!-- ❌ Mala práctica -->
<button onclick="alert('Hola')">Click</button>
// ✅ Buena práctica
document.querySelector("button").addEventListener("click", () => {
alert("Hola");
});
2. No remover eventos innecesarios
Agregar muchos addEventListener
sin eliminarlos puede causar fugas de memoria y que un evento se dispare múltiples veces.
// ❌ Se acumulan múltiples escuchas
const btn = document.querySelector("#btn");
btn.addEventListener("click", () => console.log("Click"));
btn.addEventListener("click", () => console.log("Click otra vez"));
// ✅ Usar removeEventListener cuando ya no se necesita
function handleClick() {
console.log("Click único");
btn.removeEventListener("click", handleClick);
}
btn.addEventListener("click", handleClick);
3. No usar event.preventDefault()
cuando es necesario
Algunos elementos como formularios y enlaces recargan la página por defecto, lo cual puede romper la interacción esperada.
// ❌ Formulario recarga la página
document.querySelector("form").addEventListener("submit", () => {
console.log("Enviado");
});
// ✅ Evitar recarga con preventDefault
document.querySelector("form").addEventListener("submit", (event) => {
event.preventDefault();
console.log("Formulario procesado sin recarga");
});
4. Olvidar event.stopPropagation()
En ocasiones, un evento puede propagarse a elementos padres y generar comportamientos no deseados.
// ❌ El click en el botón también activa el evento del contenedor
document.querySelector("#contenedor").addEventListener("click", () => {
console.log("Click en contenedor");
});
document.querySelector("#boton").addEventListener("click", () => {
console.log("Click en botón");
});
// ✅ Detener la propagación
document.querySelector("#boton").addEventListener("click", (event) => {
event.stopPropagation();
console.log("Click solo en botón");
});
5. No delegar eventos cuando es necesario
Si tienes muchos elementos dinámicos, agregar un addEventListener a cada uno puede ser ineficiente.
// ❌ Se agregan eventos a cada botón
document.querySelectorAll(".btn").forEach((btn) => {
btn.addEventListener("click", () => console.log("Click en botón"));
});
// ✅ Delegar evento en el contenedor
document.querySelector("#contenedor").addEventListener("click", (event) => {
if (event.target.classList.contains("btn")) {
console.log("Click en botón delegado");
}
});
Ejemplo práctico: contador con botón y eventos
let count = 0;
const button = document.querySelector("button");
const display = document.querySelector("#display");
button.addEventListener("click", () => {
count++;
display.textContent = `Contador: ${count}`;
});
Ejemplo práctico: modal emergente controlado por eventos
const openModalBtn = document.querySelector("#openModal");
const closeModalBtn = document.querySelector("#closeModal");
const modal = document.querySelector("#myModal");
openModalBtn.addEventListener("click", () => {
modal.style.display = "block";
});
closeModalBtn.addEventListener("click", () => {
modal.style.display = "none";
});
window.addEventListener("click", (event) => {
if (event.target === modal) {
modal.style.display = "none";
}
});
-
Métodos de array en JavaScript: Métodos de array en JavaScript: Guía con ejemplos y ejercicios.
-
Datos JSON en JavaScript: JSON en JavaScript: Qué es y cómo usarlo con ejemplos prácticos
-
Local Storage en JavaScript: Local Storage en JavaScript: Guía completa para principiantes
-
Más sobre eventos: mdn - Introducción a los eventos
💻 Prueba este ejercicio básico sobre eventos en JavaScript: