Manipulación del DOM: Interactuando con la Página Web
Objetivos de aprendizajeAl finalizar esta lección serás capaz de:
- Entender qué es el DOM y cómo se estructura como un árbol de nodos
- Seleccionar elementos del DOM con diferentes métodos (querySelector, getElementById, etc.)
- Crear, modificar y eliminar elementos del DOM dinámicamente
- Modificar estilos, clases y atributos de elementos HTML
- Construir interfaces dinámicas que respondan a datos cambiantes
1. ¿Qué es el DOM?
El DOM (Document Object Model) es una representación en forma de árbol de tu documento HTML. Cuando el navegador carga una página, convierte el HTML en un árbol de objetos JavaScript que podés manipular.
<!-- Este HTML... -->
<html>
<head><title>Mi Página</title></head>
<body>
<h1>Hola</h1>
<p>Mundo</p>
</body>
</html>
Se convierte en un árbol donde cada etiqueta es un "nodo":
document
└── html
├── head
│ └── title
│ └── "Mi Página" (nodo texto)
└── body
├── h1
│ └── "Hola" (nodo texto)
└── p
└── "Mundo" (nodo texto)
JavaScript puede acceder a cualquier nodo de este árbol y modificarlo: cambiar texto, agregar elementos, quitar elementos, cambiar estilos, etc.
Seleccionando elementos
// querySelector — selecciona el PRIMER elemento que coincide (CSS selector)
const titulo = document.querySelector("h1");
const primerBoton = document.querySelector(".btn");
const formulario = document.querySelector("#contacto-form");
const link = document.querySelector('a[href="#contacto"]');
const primerItem = document.querySelector("ul > li:first-child");
// querySelectorAll — selecciona TODOS los que coinciden (NodeList)
const todosLosParrafos = document.querySelectorAll("p");
const todosLosBotones = document.querySelectorAll(".btn");
const todosLosItems = document.querySelectorAll("ul li");
// Iterar sobre NodeList
todosLosParrafos.forEach(p => {
console.log(p.textContent);
});
// Convertir a array para usar map, filter, etc.
const arrParrafos = Array.from(todosLosParrafos);
const parrafosConTexto = arrParrafos.filter(p => p.textContent.length > 0);
// Métodos clásicos (todavía válidos)
const porId = document.getElementById("mi-id");
const porClase = document.getElementsByClassName("mi-clase"); // HTMLCollection
const porTag = document.getElementsByTagName("div"); // HTMLCollection
Modificar contenido
const elemento = document.querySelector("#saludo");
// textContent — texto plano (más seguro)
elemento.textContent = "Hola, JavaScript!";
// innerHTML — puede insertar HTML (cuidado con XSS)
elemento.innerHTML = "<strong>Hola</strong>, <em>JavaScript</em>!";
// innerText — similar a textContent pero respeta CSS (display:none)
// Formularios
const input = document.querySelector("#email");
input.value = "[email protected]"; // Cambiar valor
console.log(input.value); // Leer valor
const checkbox = document.querySelector("#acepto");
checkbox.checked = true;
const select = document.querySelector("#pais");
select.value = "AR";
2. Crear, agregar y eliminar elementos
Crear elementos
// Crear un elemento
const nuevoDiv = document.createElement("div");
nuevoDiv.textContent = "Soy un div nuevo";
nuevoDiv.classList.add("tarjeta");
nuevoDiv.id = "tarjeta-nueva";
nuevoDiv.style.padding = "16px";
// Crear estructura compleja
const tarjeta = document.createElement("article");
tarjeta.className = "product-card";
tarjeta.innerHTML = `
<img src="/producto.jpg" alt="Producto">
<h3>Nombre del Producto</h3>
<p class="precio">$999.99</p>
<button class="btn-comprar">Agregar al carrito</button>
`;
Agregar al DOM
const contenedor = document.querySelector("#contenedor");
// appendChild — agrega al final
contenedor.appendChild(tarjeta);
// prepend — agrega al inicio
contenedor.prepend(nuevoDiv);
// append — agrega múltiples al final (acepta texto también)
contenedor.append(elemento1, elemento2, "texto directo");
// insertBefore — insertar antes de un elemento específico
const referencia = document.querySelector("#referencia");
contenedor.insertBefore(nuevoDiv, referencia);
// insertAdjacentHTML — insertar HTML en posición específica
contenedor.insertAdjacentHTML("beforeend", "<p>Nuevo párrafo</p>");
// Posiciones: beforebegin, afterbegin, beforeend, afterend
Eliminar elementos
// remove() — el elemento se quita a sí mismo
const elemento = document.querySelector(".a-eliminar");
elemento.remove();
// removeChild() — el padre quita al hijo
const padre = document.querySelector("#lista");
const hijo = padre.querySelector("li:last-child");
padre.removeChild(hijo);
// Vaciar un contenedor
contenedor.innerHTML = ""; // Rápido pero destruye event listeners
// Forma más segura de vaciar
while (contenedor.firstChild) {
contenedor.removeChild(contenedor.firstChild);
}
Clonar elementos
const original = document.querySelector(".template");
const clon = original.cloneNode(true); // true = copia profunda (con hijos)
clon.id = "clon-1"; // Cambiar el id para evitar duplicados
contenedor.appendChild(clon);
3. Estilos, clases y atributos
Manipular clases CSS
const card = document.querySelector(".card");
// classList — la forma moderna y recomendada
card.classList.add("activa"); // Agregar clase
card.classList.remove("oculta"); // Quitar clase
card.classList.toggle("expandida"); // Toggle (agregar/quitar)
card.classList.contains("activa"); // Verificar (retorna boolean)
card.classList.replace("old", "new"); // Reemplazar una clase
// Agregar múltiples clases
card.classList.add("clase1", "clase2", "clase3");
// Ejemplo práctico: sistema de tabs
const tabs = document.querySelectorAll(".tab");
const paneles = document.querySelectorAll(".panel");
tabs.forEach(tab => {
tab.addEventListener("click", () => {
// Quitar activa de todos
tabs.forEach(t => t.classList.remove("activa"));
paneles.forEach(p => p.classList.remove("visible"));
// Activar la clickeada
tab.classList.add("activa");
const panelId = tab.dataset.panel;
document.querySelector(`#${panelId}`).classList.add("visible");
});
});
Manipular estilos inline
const box = document.querySelector(".box");
// Estilos individuales
box.style.backgroundColor = "#6366f1";
box.style.color = "white";
box.style.padding = "20px";
box.style.borderRadius = "12px";
box.style.transform = "translateY(-5px)";
box.style.transition = "all 0.3s ease";
// Leer estilos computados (incluye CSS externo)
const estilos = getComputedStyle(box);
console.log(estilos.backgroundColor); // "rgb(99, 102, 241)"
console.log(estilos.padding); // "20px"
// Aplicar múltiples estilos de una vez
Object.assign(box.style, {
width: "300px",
height: "200px",
display: "flex",
alignItems: "center",
justifyContent: "center"
});
Data attributes
<div id="producto"
data-id="123"
data-precio="999"
data-en-oferta="true"
data-categoria="tech">
Laptop HP
</div>
const producto = document.querySelector("#producto");
// Leer data attributes
console.log(producto.dataset.id); // "123"
console.log(producto.dataset.precio); // "999"
console.log(producto.dataset.enOferta); // "true" (camelCase automático)
console.log(producto.dataset.categoria); // "tech"
// Modificar
producto.dataset.precio = "899";
producto.dataset.nuevo = "sí"; // Crea data-nuevo="sí"
// Eliminar
delete producto.dataset.enOferta;
Ejemplo completo: Renderizar una lista desde datos
const productos = [
{ id: 1, nombre: "Laptop", precio: 999, stock: 5, imagen: "laptop.jpg" },
{ id: 2, nombre: "Mouse", precio: 29, stock: 0, imagen: "mouse.jpg" },
{ id: 3, nombre: "Teclado", precio: 79, stock: 12, imagen: "teclado.jpg" },
{ id: 4, nombre: "Monitor", precio: 450, stock: 3, imagen: "monitor.jpg" },
];
function renderizarProductos(productos, contenedorId) {
const contenedor = document.querySelector(`#${contenedorId}`);
contenedor.innerHTML = ""; // Limpiar
if (productos.length === 0) {
contenedor.innerHTML = '<p class="vacio">No hay productos disponibles</p>';
return;
}
productos.forEach(prod => {
const card = document.createElement("div");
card.className = "product-card";
card.dataset.id = prod.id;
card.innerHTML = `
<h3>${prod.nombre}</h3>
<p class="precio">$${prod.precio.toFixed(2)}</p>
<p class="stock ${prod.stock === 0 ? 'agotado' : ''}">
${prod.stock > 0 ? `${prod.stock} disponibles` : 'Agotado'}
</p>
<button class="btn-agregar" ${prod.stock === 0 ? 'disabled' : ''}>
${prod.stock > 0 ? 'Agregar al carrito' : 'Sin stock'}
</button>
`;
// Agregar evento al botón
const btn = card.querySelector(".btn-agregar");
if (prod.stock > 0) {
btn.addEventListener("click", () => {
console.log(`Agregado al carrito: ${prod.nombre}`);
});
}
contenedor.appendChild(card);
});
}
renderizarProductos(productos, "catalogo");
Errores comunes de principiantes
Manipular el DOM antes de que cargue: Poné tu
<script>al final del body o usádefer.Usar innerHTML para texto del usuario: Riesgo de XSS (Cross-Site Scripting). Usá
textContentpara texto plano.Seleccionar elementos en un loop sin necesidad: Guardá la referencia en una variable si vas a usar el mismo elemento múltiples veces.
No cachear selectores:
document.querySelectorrecorre el DOM cada vez. Guardá el resultado en una constante.Olvidar que
getElementsByClassNameretorna HTMLCollection (live): Los cambios en el DOM se reflejan automáticamente en la colección, lo que puede causar bugs en loops.
Puntos clave de esta lección
- El DOM es un árbol de objetos que representa tu HTML — JavaScript puede modificar cualquier nodo.
querySelector()yquerySelectorAll()son los métodos modernos para seleccionar elementos.textContentes más seguro queinnerHTMLpara insertar texto plano.classListes la forma moderna de manipular clases CSS (add, remove, toggle, contains).datasetpermite leer y escribir atributosdata-*de forma limpia.- Siempre cachéa tus selectores en variables para mejor rendimiento.
- Usá
createElement+appendChildpara crear elementos dinámicos de forma segura.
Quiz de autoevaluación
1. ¿Cuál es la diferencia entre textContent e innerHTML?
a) No hay diferencia
b) textContent inserta texto plano, innerHTML puede insertar HTML
c) innerHTML es más seguro
d) textContent solo funciona con divs
2. ¿Qué retorna document.querySelectorAll('p')?
a) Un array
b) Un NodeList
c) Un string
d) Un HTMLElement
3. ¿Cómo se agrega una clase CSS a un elemento?
a) elemento.class = 'nueva'
b) elemento.classList.add('nueva')
c) elemento.addClassName('nueva')
d) elemento.style.class = 'nueva'
4. ¿Cómo se accede a data-user-id en JavaScript?
a) elemento.data.userId
b) elemento.dataset.userId
c) elemento.getAttribute('userId')
d) elemento.dataUserId
5. ¿Qué método elimina un elemento del DOM?
a) elemento.delete()
b) elemento.remove()
c) elemento.destroy()
d) DOM.remove(elemento)
Respuestas: 1-b, 2-b, 3-b, 4-b, 5-b
Revisemos los puntos más importantes de esta lección antes de continuar.
Ejercicio práctico
Misión: Lista de tareas interactiva (sin frameworks)
Creá una página web completa con HTML, CSS y JavaScript que tenga:
- Un input y botón para agregar tareas
- Una lista que muestre las tareas con checkbox para completar
- Botón para eliminar cada tarea
- Contador de tareas pendientes/completadas
- Filtros: Todas, Pendientes, Completadas
- Las tareas completadas deben tener estilo diferente (tachado, color gris)
- Botón "Limpiar completadas" para eliminar las que ya se hicieron
Este es el proyecto clásico para practicar DOM manipulation.
- Comprendo el concepto principal y puedo explicarlo con mis palabras
- Entiendo cómo aplicarlo en mi situación específica
- Necesito repasar algunas partes antes de continuar
- Quiero ver más ejemplos prácticos del tema