Volver al curso

JavaScript Desde Cero: Tu Primer Lenguaje de Programación

leccion
13 / 22
beginner
8 horas
Arrays, Objetos y el DOM

Manipulación del DOM: Interactuando con la Página Web

Lectura
45 min~7 min lectura

Manipulación del DOM: Interactuando con la Página Web

Objetivos de aprendizaje

Al 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
  1. Manipular el DOM antes de que cargue: Poné tu <script> al final del body o usá defer.

  2. Usar innerHTML para texto del usuario: Riesgo de XSS (Cross-Site Scripting). Usá textContent para texto plano.

  3. Seleccionar elementos en un loop sin necesidad: Guardá la referencia en una variable si vas a usar el mismo elemento múltiples veces.

  4. No cachear selectores: document.querySelector recorre el DOM cada vez. Guardá el resultado en una constante.

  5. Olvidar que getElementsByClassName retorna 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
  1. El DOM es un árbol de objetos que representa tu HTML — JavaScript puede modificar cualquier nodo.
  2. querySelector() y querySelectorAll() son los métodos modernos para seleccionar elementos.
  3. textContent es más seguro que innerHTML para insertar texto plano.
  4. classList es la forma moderna de manipular clases CSS (add, remove, toggle, contains).
  5. dataset permite leer y escribir atributos data-* de forma limpia.
  6. Siempre cachéa tus selectores en variables para mejor rendimiento.
  7. Usá createElement + appendChild para 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


💡 Concepto Clave

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:

  1. Un input y botón para agregar tareas
  2. Una lista que muestre las tareas con checkbox para completar
  3. Botón para eliminar cada tarea
  4. Contador de tareas pendientes/completadas
  5. Filtros: Todas, Pendientes, Completadas
  6. Las tareas completadas deben tener estilo diferente (tachado, color gris)
  7. Botón "Limpiar completadas" para eliminar las que ya se hicieron

Este es el proyecto clásico para practicar DOM manipulation.

🧠 Pon a prueba tu conocimiento
¿Cuál es el aspecto más importante que aprendiste en esta lección?
  • 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
✅ ¡Excelente! Continúa con la siguiente lección para profundizar más.