Saltar al contenido principal

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

Eventos del DOM

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:

  1. 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.

  2. 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.

  3. 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 usar event.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 tecla Shift está presionada.
  • event.ctrlKey: Indica si la tecla Control está presionada.
  • event.altKey: Indica si la tecla Alt 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";
}
});