DOM Básico

Intermedio
JavaScript
JavaScript
Actualizado: 27/08/2025

Selección de elementos

El Document Object Model (DOM) es la representación en memoria de la estructura HTML de una página web. JavaScript utiliza el DOM como puente para interactuar con los elementos HTML, permitiéndonos modificar contenido, estilos y responder a eventos del usuario. Imagina el DOM como un árbol genealógico donde cada elemento HTML es un nodo que puede tener elementos padre, hijos y hermanos.

Para trabajar con elementos HTML desde JavaScript, primero necesitamos seleccionarlos. La selección de elementos es el primer paso fundamental para cualquier manipulación del DOM, ya que nos proporciona referencias a los elementos específicos con los que queremos trabajar.

Selección por ID

El método más directo para seleccionar un elemento específico es utilizando su identificador único. El método getElementById() busca y retorna el elemento que tenga el ID especificado:

// HTML: <h1 id="titulo-principal">Mi aplicación</h1>
const titulo = document.getElementById("titulo-principal");
console.log(titulo); // Retorna el elemento <h1> o null si no existe

Este método es muy eficiente porque los IDs son únicos en el documento. Si no encuentra el elemento, retorna null en lugar de generar un error, lo que nos permite manejar casos donde el elemento podría no existir:

const elemento = document.getElementById("elemento-inexistente");
if (elemento) {
    // El elemento existe, podemos trabajar con él
    elemento.style.color = "blue";
} else {
    console.log("Elemento no encontrado");
}

Selección con selectores CSS

Los métodos querySelector() y querySelectorAll() representan la forma más versátil y moderna de seleccionar elementos. Utilizan la misma sintaxis que los selectores CSS, proporcionando gran flexibilidad:

**querySelector()** retorna el primer elemento que coincida con el selector especificado:

// Seleccionar por clase
const boton = document.querySelector(".btn-primary");

// Seleccionar por atributo
const input = document.querySelector("input[type='email']");

// Seleccionar con combinadores
const primerParrafo = document.querySelector("article > p");

// Seleccionar por ID (alternativa a getElementById)
const header = document.querySelector("#header");

**querySelectorAll()** retorna una NodeList con todos los elementos que coincidan con el selector. Aunque parece un array, es una estructura similar que podemos recorrer:

// HTML: <li class="item">Elemento 1</li>
//       <li class="item">Elemento 2</li>
//       <li class="item">Elemento 3</li>

const items = document.querySelectorAll(".item");
console.log(items.length); // 3

// Recorrer con for...of
for (const item of items) {
    item.style.backgroundColor = "lightblue";
}

// Recorrer con forEach
items.forEach((item, index) => {
    item.textContent = `Item modificado ${index + 1}`;
});

Selectores avanzados

La potencia de querySelector radica en su capacidad para usar cualquier selector CSS válido, permitiendo selecciones muy específicas:

// Seleccionar elementos anidados
const enlaceEnNav = document.querySelector("nav ul li a");

// Combinadores de hermanos
const siguienteElemento = document.querySelector("h2 + p");

// Pseudoselectores
const primerHijo = document.querySelector("ul li:first-child");
const ultimoElemento = document.querySelector("div:last-of-type");

// Selectores de atributos complejos
const enlacesExternos = document.querySelectorAll("a[href^='http']");
const inputsRequeridos = document.querySelectorAll("input[required]");

Métodos tradicionales por colección

Aunque menos utilizados en código moderno, estos métodos siguen siendo válidos para casos específicos:

**getElementsByClassName()** retorna una HTMLCollection viva de elementos con la clase especificada:

const elementos = document.getElementsByClassName("destacado");
// Retorna una colección que se actualiza automáticamente

**getElementsByTagName()** selecciona elementos por su etiqueta HTML:

const parrafos = document.getElementsByTagName("p");
const imagenes = document.getElementsByTagName("img");

La diferencia principal es que estas colecciones son "vivas", es decir, se actualizan automáticamente cuando el DOM cambia, mientras que querySelectorAll() retorna una lista estática.

Selección en contexto

Podemos limitar nuestras búsquedas a un elemento específico en lugar de todo el documento, lo que mejora el rendimiento y la precisión:

// HTML: <div id="sidebar">
//         <ul>
//           <li class="active">Inicio</li>
//           <li>Productos</li>
//         </ul>
//       </div>

const sidebar = document.getElementById("sidebar");
const elementoActivo = sidebar.querySelector(".active");
const todosLosItems = sidebar.querySelectorAll("li");

Este enfoque es especialmente útil cuando trabajamos con componentes específicos de la página, evitando conflictos con elementos similares en otras partes del documento.

Validación de selecciones

Siempre debemos verificar que nuestras selecciones fueron exitosas antes de manipular los elementos:

function cambiarTexto(selector, nuevoTexto) {
    const elemento = document.querySelector(selector);
    
    if (elemento) {
        elemento.textContent = nuevoTexto;
        return true;
    } else {
        console.warn(`No se encontró elemento con selector: ${selector}`);
        return false;
    }
}

// Uso seguro
cambiarTexto("#mensaje", "¡Texto actualizado!");

Esta práctica previene errores cuando intentamos manipular elementos que no existen, haciendo nuestro código más robusto y mantenible.

Manipulación de contenido y atributos

Una vez que hemos seleccionado los elementos del DOM, el siguiente paso es modificar su contenido y atributos. JavaScript proporciona múltiples propiedades y métodos para manipular tanto el texto como las características de los elementos HTML de forma dinámica.

Modificación de contenido de texto

La propiedad **textContent** es la forma más segura y directa de trabajar con el contenido de texto de un elemento. Esta propiedad obtiene o establece únicamente el texto, ignorando cualquier etiqueta HTML:

const titulo = document.querySelector("h1");

// Leer el contenido actual
console.log(titulo.textContent); // "Mi título original"

// Modificar el contenido
titulo.textContent = "Nuevo título dinámico";

La ventaja principal de textContent es que escapa automáticamente cualquier código HTML, evitando problemas de seguridad:

const mensaje = document.querySelector("#mensaje");

// Esto se mostrará como texto literal, no como HTML
mensaje.textContent = "<script>alert('XSS')</script>";
// Resultado en pantalla: <script>alert('XSS')</script>

Manipulación de contenido HTML

Para insertar contenido HTML real, utilizamos la propiedad innerHTML. Esta propiedad interpreta las etiquetas HTML y las renderiza como elementos:

const contenedor = document.querySelector(".contenido");

// Insertar HTML
contenedor.innerHTML = `
    <p>Este es un <strong>párrafo</strong> con formato.</p>
    <ul>
        <li>Elemento de lista 1</li>
        <li>Elemento de lista 2</li>
    </ul>
`;

Podemos combinar innerHTML con template literals para crear contenido dinámico de forma elegante:

const productos = [
    { nombre: "Laptop", precio: 899 },
    { nombre: "Mouse", precio: 25 },
    { nombre: "Teclado", precio: 75 }
];

const lista = document.querySelector("#lista-productos");

lista.innerHTML = productos
    .map(producto => `
        <div class="producto">
            <h3>${producto.nombre}</h3>
            <span class="precio">${producto.precio}€</span>
        </div>
    `)
    .join("");

Manipulación segura con insertAdjacentHTML

El método **insertAdjacentHTML()** ofrece mayor control sobre dónde insertar contenido HTML, sin sobrescribir el contenido existente:

const lista = document.querySelector("ul");

// Insertar al final de la lista
lista.insertAdjacentHTML("beforeend", "<li>Nuevo elemento al final</li>");

// Insertar al principio
lista.insertAdjacentHTML("afterbegin", "<li>Nuevo elemento al principio</li>");

// Insertar antes del elemento
lista.insertAdjacentHTML("beforebegin", "<p>Párrafo antes de la lista</p>");

// Insertar después del elemento
lista.insertAdjacentHTML("afterend", "<p>Párrafo después de la lista</p>");

Gestión de atributos

Los atributos HTML se manipulan mediante métodos específicos que proporcionan control total sobre las características de los elementos.

**getAttribute()** y **setAttribute()** son los métodos fundamentales para trabajar con atributos:

const imagen = document.querySelector("img");

// Leer atributos existentes
const rutaActual = imagen.getAttribute("src");
const textoAlt = imagen.getAttribute("alt");

// Establecer nuevos atributos
imagen.setAttribute("src", "/images/nueva-imagen.jpg");
imagen.setAttribute("alt", "Descripción actualizada");
imagen.setAttribute("loading", "lazy");

Para eliminar atributos, utilizamos removeAttribute():

const enlace = document.querySelector("a");

// Eliminar atributo
enlace.removeAttribute("target");

// Verificar si un atributo existe
if (enlace.hasAttribute("download")) {
    console.log("El enlace es una descarga");
}

Manipulación de clases CSS

La propiedad **classList** proporciona métodos específicos para trabajar con clases CSS de forma más elegante que manipular directamente el atributo class:

const elemento = document.querySelector(".card");

// Agregar clases
elemento.classList.add("activo", "destacado");

// Eliminar clases
elemento.classList.remove("inactivo");

// Alternar clase (agregar si no existe, eliminar si existe)
elemento.classList.toggle("expandido");

// Verificar si tiene una clase
if (elemento.classList.contains("activo")) {
    console.log("El elemento está activo");
}

// Reemplazar una clase por otra
elemento.classList.replace("tema-claro", "tema-oscuro");

Manipulación de estilos inline

La propiedad **style** permite modificar estilos CSS directamente desde JavaScript. Los nombres de propiedades CSS se convierten a camelCase:

const caja = document.querySelector(".caja");

// Establecer estilos individuales
caja.style.backgroundColor = "lightblue";
caja.style.padding = "20px";
caja.style.borderRadius = "10px";

// Establecer múltiples estilos
Object.assign(caja.style, {
    width: "300px",
    height: "200px",
    display: "flex",
    justifyContent: "center",
    alignItems: "center"
});

Para remover estilos inline, asignamos una cadena vacía:

// Eliminar estilos específicos
caja.style.backgroundColor = "";
caja.style.padding = "";

// O usar removeProperty()
caja.style.removeProperty("background-color");

Propiedades específicas de elementos

Algunos elementos HTML tienen propiedades específicas que facilitan su manipulación:

// Elementos de formulario
const input = document.querySelector("input[type='text']");
input.value = "Nuevo valor";
input.placeholder = "Escribe aquí...";
input.disabled = true;

// Checkboxes y radio buttons
const checkbox = document.querySelector("input[type='checkbox']");
checkbox.checked = true;

// Elementos de imagen
const img = document.querySelector("img");
img.src = "/images/nueva.jpg";
img.alt = "Nueva descripción";

// Enlaces
const enlace = document.querySelector("a");
enlace.href = "https://ejemplo.com";
enlace.target = "_blank";

Manipulación de atributos de datos

Los atributos de datos (data-*) son especialmente útiles para almacenar información personalizada en elementos HTML:

const producto = document.querySelector(".producto");

// Establecer atributos de datos
producto.dataset.id = "12345";
producto.dataset.categoria = "electronica";
producto.dataset.precio = "299.99";

// Leer atributos de datos
console.log(producto.dataset.id); // "12345"
console.log(producto.dataset.categoria); // "electronica"

// Los guiones se convierten en camelCase
producto.setAttribute("data-fecha-creacion", "2024-01-15");
console.log(producto.dataset.fechaCreacion); // "2024-01-15"

Creación y manipulación de elementos nuevos

Podemos crear elementos completamente nuevos y configurar sus propiedades antes de insertarlos en el DOM:

// Crear nuevo elemento
const nuevoParrafo = document.createElement("p");

// Configurar contenido y atributos
nuevoParrafo.textContent = "Este es un párrafo creado dinámicamente";
nuevoParrafo.classList.add("parrafo-dinamico");
nuevoParrafo.setAttribute("data-origen", "javascript");

// Insertar en el documento
const contenedor = document.querySelector(".contenido");
contenedor.appendChild(nuevoParrafo);

Para elementos más complejos, podemos usar template literals con innerHTML:

const tarjeta = document.createElement("div");
tarjeta.className = "tarjeta-producto";
tarjeta.innerHTML = `
    <img src="/images/producto.jpg" alt="Producto">
    <h3>Nombre del producto</h3>
    <p class="precio">€99.99</p>
    <button class="btn-comprar">Comprar</button>
`;

document.querySelector(".productos").appendChild(tarjeta);

Buenas prácticas para la manipulación

Al manipular contenido y atributos, es importante seguir ciertas prácticas recomendadas:

  • Validar elementos antes de manipularlos para evitar errores:
function actualizarElemento(selector, contenido) {
    const elemento = document.querySelector(selector);
    if (elemento) {
        elemento.textContent = contenido;
    } else {
        console.warn(`Elemento no encontrado: ${selector}`);
    }
}

Preferir **textContent** sobre **innerHTML** cuando solo necesitamos texto, por razones de seguridad y rendimiento.

Usar **classList** en lugar de manipular directamente el atributo class.

Agrupar múltiples modificaciones para minimizar el repintado del navegador:

const elemento = document.querySelector(".elemento");

// Mejor: agrupar cambios
elemento.style.cssText = `
    width: 200px;
    height: 100px;
    background-color: blue;
    border-radius: 5px;
`;

// Que hacer cambios individuales
// elemento.style.width = "200px";
// elemento.style.height = "100px";
// etc...

Eventos básicos (click, submit, change)

Los eventos son acciones que ocurren en la página web, como hacer clic en un botón, enviar un formulario o cambiar el valor de un campo. JavaScript nos permite escuchar estos eventos y responder a ellos ejecutando código específico, creando así interactividad real en nuestras aplicaciones web.

Fundamentos de eventos con addEventListener

El método **addEventListener()** es la forma moderna y recomendada de manejar eventos en JavaScript. Este método permite asociar funciones específicas (llamadas event listeners) a eventos particulares de los elementos:

const boton = document.querySelector("#mi-boton");

boton.addEventListener("click", function() {
    console.log("¡Botón clickeado!");
});

La sintaxis básica de addEventListener() requiere dos parámetros principales: el tipo de evento (como string) y la función que se ejecutará cuando ocurra el evento. Esta aproximación es más flexible que los antiguos manejadores inline y permite múltiples listeners para el mismo evento.

Evento click

El evento **click** es probablemente el más utilizado en aplicaciones web. Se dispara cuando un usuario hace clic con el botón primario del mouse sobre un elemento:

const botonSaludo = document.querySelector(".btn-saludo");

botonSaludo.addEventListener("click", function() {
    const mensaje = document.querySelector("#mensaje");
    mensaje.textContent = "¡Hola desde JavaScript!";
    mensaje.classList.add("activo");
});

Podemos usar arrow functions para una sintaxis más concisa, especialmente útil en código moderno:

const contador = document.querySelector("#contador");
let clicks = 0;

contador.addEventListener("click", () => {
    clicks++;
    contador.textContent = `Clicks: ${clicks}`;
});

Trabajar con el objeto Event

Cada evento proporciona un objeto event que contiene información útil sobre lo que ocurrió. Este objeto se pasa automáticamente como primer parámetro a nuestra función manejadora:

const enlace = document.querySelector("a");

enlace.addEventListener("click", (event) => {
    // Prevenir comportamiento por defecto
    event.preventDefault();
    
    console.log("Enlace clickeado:", event.target.href);
    console.log("Posición del click:", event.clientX, event.clientY);
});

El método **preventDefault()** es especialmente útil para evitar comportamientos por defecto, como la navegación automática de los enlaces o el envío automático de formularios.

Evento submit en formularios

El evento **submit** se dispara cuando un formulario está a punto de enviarse. Es el momento ideal para validar datos antes de enviarlos al servidor:

const formulario = document.querySelector("#formulario-contacto");

formulario.addEventListener("submit", (event) => {
    event.preventDefault(); // Evitar envío automático
    
    // Obtener datos del formulario
    const nombre = document.querySelector("#nombre").value;
    const email = document.querySelector("#email").value;
    
    // Validación básica
    if (!nombre || !email) {
        mostrarError("Todos los campos son obligatorios");
        return;
    }
    
    // Validar formato de email
    if (!email.includes("@")) {
        mostrarError("Email no válido");
        return;
    }
    
    // Procesar datos válidos
    procesarFormulario(nombre, email);
});

function mostrarError(mensaje) {
    const errorDiv = document.querySelector("#error");
    errorDiv.textContent = mensaje;
    errorDiv.classList.add("visible");
}

function procesarFormulario(nombre, email) {
    console.log("Datos válidos:", { nombre, email });
    // Aquí enviarías los datos al servidor
}

Acceso a datos de formulario con FormData

Para formularios más complejos, podemos utilizar la API FormData que simplifica la recolección de todos los campos:

const formulario = document.querySelector("#formulario-completo");

formulario.addEventListener("submit", (event) => {
    event.preventDefault();
    
    // FormData recolecta automáticamente todos los campos con nombre
    const formData = new FormData(formulario);
    
    // Convertir a objeto para facilitar el trabajo
    const datos = Object.fromEntries(formData);
    
    console.log("Datos del formulario:", datos);
    
    // Validar y procesar datos
    if (validarDatos(datos)) {
        enviarDatos(datos);
    }
});

function validarDatos(datos) {
    // Lógica de validación
    return datos.email && datos.nombre && datos.mensaje;
}

Evento change en campos de entrada

El evento **change** se dispara cuando el valor de un campo de entrada cambia y el usuario sale del campo (pierde el foco). Es útil para validación en tiempo real y actualizaciones dinámicas:

const campoEmail = document.querySelector("#email");

campoEmail.addEventListener("change", (event) => {
    const email = event.target.value;
    const mensajeValidacion = document.querySelector("#validacion-email");
    
    if (validarEmail(email)) {
        mensajeValidacion.textContent = "✓ Email válido";
        mensajeValidacion.className = "mensaje-exito";
    } else {
        mensajeValidacion.textContent = "✗ Email no válido";
        mensajeValidacion.className = "mensaje-error";
    }
});

function validarEmail(email) {
    return email.includes("@") && email.includes(".");
}

Diferencia entre change e input

Para una respuesta inmediata mientras el usuario escribe, utilizamos el evento input en lugar de change:

const campoNombre = document.querySelector("#nombre");
const contadorCaracteres = document.querySelector("#contador-caracteres");

campoNombre.addEventListener("input", (event) => {
    const longitud = event.target.value.length;
    contadorCaracteres.textContent = `${longitud}/50 caracteres`;
    
    // Cambiar color según la longitud
    if (longitud > 45) {
        contadorCaracteres.classList.add("alerta");
    } else {
        contadorCaracteres.classList.remove("alerta");
    }
});

Eventos en elementos de selección

Los elementos **select** también utilizan el evento change para detectar cuando el usuario selecciona una opción diferente:

const selectorPais = document.querySelector("#pais");
const infoPais = document.querySelector("#info-pais");

selectorPais.addEventListener("change", (event) => {
    const paisSeleccionado = event.target.value;
    
    // Mostrar información basada en la selección
    switch (paisSeleccionado) {
        case "es":
            infoPais.textContent = "Moneda: Euro (€)";
            break;
        case "us":
            infoPais.textContent = "Moneda: Dólar ($)";
            break;
        case "uk":
            infoPais.textContent = "Moneda: Libra (£)";
            break;
        default:
            infoPais.textContent = "Selecciona un país";
    }
});

Manejo de checkboxes y radio buttons

Los checkboxes y radio buttons también responden al evento change, pero requieren verificar su estado:

const checkboxes = document.querySelectorAll("input[type='checkbox']");
const resumenSeleccion = document.querySelector("#resumen");

checkboxes.forEach(checkbox => {
    checkbox.addEventListener("change", (event) => {
        actualizarResumen();
    });
});

function actualizarResumen() {
    const seleccionados = document.querySelectorAll("input[type='checkbox']:checked");
    const valores = Array.from(seleccionados).map(cb => cb.value);
    
    resumenSeleccion.textContent = valores.length > 0 
        ? `Seleccionados: ${valores.join(", ")}`
        : "Ninguna opción seleccionada";
}

Remover event listeners

Es importante limpiar los event listeners cuando ya no los necesitamos para evitar memory leaks:

const boton = document.querySelector("#boton-temporal");

function manejadorClick() {
    console.log("Click manejado");
    
    // Remover el listener después del primer click
    boton.removeEventListener("click", manejadorClick);
    boton.textContent = "Ya no responde a clicks";
}

boton.addEventListener("click", manejadorClick);

Delegación de eventos

Para elementos que se crean dinámicamente, podemos usar delegación de eventos escuchando en un elemento padre:

const contenedorLista = document.querySelector("#lista-tareas");

// Escuchar clicks en el contenedor, no en elementos individuales
contenedorLista.addEventListener("click", (event) => {
    // Verificar si el click fue en un botón de eliminar
    if (event.target.classList.contains("btn-eliminar")) {
        const tarea = event.target.closest(".tarea");
        tarea.remove();
    }
    
    // Verificar si el click fue en un checkbox
    if (event.target.type === "checkbox") {
        const tarea = event.target.closest(".tarea");
        tarea.classList.toggle("completada");
    }
});

// Función para agregar nuevas tareas dinámicamente
function agregarTarea(texto) {
    const nuevaTarea = document.createElement("div");
    nuevaTarea.className = "tarea";
    nuevaTarea.innerHTML = `
        <input type="checkbox">
        <span>${texto}</span>
        <button class="btn-eliminar">Eliminar</button>
    `;
    contenedorLista.appendChild(nuevaTarea);
}

Ejemplo práctico integrado

Combinemos todo lo aprendido en un ejemplo práctico de una aplicación de notas:

// Seleccionar elementos
const formularioNota = document.querySelector("#formulario-nota");
const inputTitulo = document.querySelector("#titulo-nota");
const textareaContenido = document.querySelector("#contenido-nota");
const contenedorNotas = document.querySelector("#contenedor-notas");
const contadorCaracteres = document.querySelector("#contador");

// Contador de caracteres en tiempo real
textareaContenido.addEventListener("input", (event) => {
    const longitud = event.target.value.length;
    contadorCaracteres.textContent = `${longitud}/500 caracteres`;
    
    if (longitud > 450) {
        contadorCaracteres.classList.add("limite-cercano");
    } else {
        contadorCaracteres.classList.remove("limite-cercano");
    }
});

// Manejar envío del formulario
formularioNota.addEventListener("submit", (event) => {
    event.preventDefault();
    
    const titulo = inputTitulo.value.trim();
    const contenido = textareaContenido.value.trim();
    
    // Validación
    if (!titulo || !contenido) {
        mostrarMensaje("Título y contenido son obligatorios", "error");
        return;
    }
    
    // Crear nueva nota
    crearNota(titulo, contenido);
    
    // Limpiar formulario
    formularioNota.reset();
    contadorCaracteres.textContent = "0/500 caracteres";
    
    mostrarMensaje("Nota creada exitosamente", "exito");
});

// Delegación para botones de eliminar notas
contenedorNotas.addEventListener("click", (event) => {
    if (event.target.classList.contains("btn-eliminar-nota")) {
        const nota = event.target.closest(".nota");
        nota.remove();
        mostrarMensaje("Nota eliminada", "info");
    }
});

function crearNota(titulo, contenido) {
    const nota = document.createElement("div");
    nota.className = "nota";
    nota.innerHTML = `
        <h3>${titulo}</h3>
        <p>${contenido}</p>
        <button class="btn-eliminar-nota">Eliminar</button>
        <small>Creada: ${new Date().toLocaleString()}</small>
    `;
    contenedorNotas.appendChild(nota);
}

function mostrarMensaje(texto, tipo) {
    const mensaje = document.createElement("div");
    mensaje.className = `mensaje ${tipo}`;
    mensaje.textContent = texto;
    document.body.appendChild(mensaje);
    
    // Remover mensaje después de 3 segundos
    setTimeout(() => {
        mensaje.remove();
    }, 3000);
}

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en JavaScript

Documentación oficial de JavaScript
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, JavaScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de JavaScript

Explora más contenido relacionado con JavaScript y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender qué es el DOM y cómo seleccionar elementos HTML mediante diferentes métodos.
  • Aprender a modificar contenido, atributos, clases y estilos de los elementos del DOM.
  • Conocer la creación dinámica de elementos y buenas prácticas para manipular el DOM.
  • Entender el manejo básico de eventos como click, submit y change para crear interactividad.
  • Aplicar técnicas de validación y delegación de eventos para mejorar la robustez y eficiencia del código.