El DOM en JavaScript
En esta lección
Cuando el navegador carga una página, lee el HTML y construye en memoria un árbol de objetos que representa cada elemento, cada atributo y cada trozo de texto. Ese árbol es el DOM (Document Object Model), y es un estándar del W3C que siguen todos los navegadores.
Lo importante: el navegador dibuja la pantalla a partir del DOM, no del HTML. Si JavaScript cambia el DOM, la pantalla cambia al instante, sin recargar la página. El archivo HTML no se toca.
document
└── html
├── head
│ └── title → "Mi página"
└── body
├── h1 → "Hola"
└── p → "Un párrafo"
Cada elemento HTML tiene su objeto en el DOM, y cada atributo del HTML es una propiedad de ese objeto (img.src, a.href, input.value…).
Cómo se ejecuta JavaScript en una página
La vida de un programa en el navegador tiene dos fases:
- Carga (secuencial). El navegador lee el HTML y ejecuta los scripts en el orden en que aparecen. Dura unas décimas de segundo. En esta fase se preparan las cosas y se registran los manejadores de eventos.
- Eventos (asíncrona). A partir de ahí, el código solo se ejecuta cuando pasa algo: un clic, una tecla, la respuesta de un servidor, un temporizador… Dura todo el tiempo que la página está abierta.
Por eso, casi todo tu código de interfaz serán funciones que se ejecutan en respuesta a eventos.
Los objetos globales
window: la ventana (o pestaña) del navegador. Contiene todo lo demás:window.alert,window.innerWidth,window.localStorage… Como es global, se puede omitir:alert()eswindow.alert().document: la página cargada. Es la puerta de entrada al DOM.
Recuerda que todos los scripts de una misma página comparten el mismo ámbito global: aunque tengas varios archivos .js, si están incluidos en el mismo HTML es como si fueran uno solo (salvo que uses módulos).
Seleccionar elementos
// El PRIMER elemento que coincide con un selector CSS (o null si no hay ninguno)
const titulo = document.querySelector("h1");
const boton = document.querySelector("#enviar"); // ¡la almohadilla!
const aviso = document.querySelector(".aviso");
const campo = document.querySelector("form input[name=email]");
// TODOS los que coinciden
const enlaces = document.querySelectorAll("nav a");
enlaces.length; // cuántos hay (0 si ninguno)
for (const enlace of enlaces) {
console.log(enlace.href);
}
Los selectores son exactamente los mismos que en CSS. Por eso aprender bien los selectores te sirve dos veces.
querySelectorAll devuelve una NodeList: se parece a un array (tiene length, se recorre con for...of y forEach) pero no tiene todos sus métodos. Si necesitas map o filter, conviértela: [...enlaces].filter(...).
También puedes buscar dentro de un elemento, no solo en todo el documento:
const menu = document.querySelector("#menu");
const opciones = menu.querySelectorAll("li");
Evita los métodos antiguos
En tutoriales antiguos (y a veces en código generado por IA) verás getElementById, getElementsByClassName, getElementsByTagName o document.forms. Funcionan, pero querySelector y querySelectorAll hacen lo mismo y más, con una sola sintaxis. Úsalos siempre.
Un error muy frecuente
const boton = document.querySelector("enviar"); // ✗ falta la #
boton.addEventListener("click", ...); // TypeError: Cannot read properties of null
Si querySelector devuelve null, revisa: ¿falta # o .? ¿hay una errata en el nombre? ¿se está ejecutando el script antes de que exista el elemento? (Pon el script al final del <body> o usa defer.) Cuando empieces, pon un console.log(boton) después de cada selección: te ahorrará mucho tiempo.
Cambiar el contenido
titulo.textContent = "Nuevo título"; // cambia el texto
console.log(titulo.textContent); // también sirve para leerlo
aviso.innerHTML = "Guardado <strong>correctamente</strong>"; // interpreta etiquetas
Usa textContent siempre que puedas. innerHTML interpreta HTML, y si metes ahí algo que ha escrito un usuario, puede inyectar código malicioso en tu página (un ataque llamado XSS). Para construir contenido con etiquetas, crea los elementos como verás en la lección de crear elementos.
Cuidado con las erratas: si escribes titulo.textContet = "...", JavaScript no da error; simplemente crea una propiedad nueva que no sirve para nada y el texto no cambia.
Cambiar clases
La mejor forma de cambiar el aspecto de un elemento es añadir o quitar clases y dejar el diseño en el CSS, igual que en un procesador de textos es mejor aplicar un estilo que cambiar el formato a mano:
.oculto { display: none; }
.activo { background: #ffe845; }
aviso.classList.add("activo");
aviso.classList.remove("oculto");
aviso.classList.toggle("activo"); // la pone si no está, la quita si está
aviso.classList.contains("activo"); // true o false
classList es la lista de clases del atributo class. (Existe también la propiedad className, que es el atributo class como texto completo; se llama así porque class es una palabra reservada de JavaScript. classList es mucho más cómoda.)
Cambiar estilos directamente
Si de verdad necesitas cambiar un estilo concreto (por ejemplo, una posición calculada), usa style. Las propiedades CSS con guion se escriben en camelCase:
aviso.style.backgroundColor = "lightyellow"; // background-color
aviso.style.borderWidth = "2px"; // border-width
aviso.style.display = "none";
Prefiere las clases: el CSS queda en un solo sitio y es más fácil de mantener.
Atributos
const imagen = document.querySelector("img");
imagen.src = "img/otra-foto.jpg";
imagen.alt = "Descripción de la nueva foto";
const enlace = document.querySelector("a");
enlace.href; // la URL completa
enlace.getAttribute("href"); // el valor tal cual está en el HTML
enlace.setAttribute("target", "_blank");
enlace.removeAttribute("target");
const campo = document.querySelector("#email");
campo.value; // lo que hay escrito
campo.disabled = true; // los atributos booleanos son true / false
Dos propiedades no se llaman igual que su atributo, porque el nombre es una palabra reservada: class es className y el for de un <label> es htmlFor.
Los atributos data-* se leen con dataset:
<button data-producto-id="42" data-precio="19.99">Añadir</button>
boton.dataset.productoId // "42" (siempre texto)
boton.dataset.precio // "19.99"
Moverse por el árbol
Desde un elemento puedes llegar a sus vecinos:
elemento.parentElement // el padre
elemento.children // los hijos (elementos)
elemento.firstElementChild // el primer hijo
elemento.nextElementSibling // el siguiente hermano
elemento.closest(".tarjeta") // el ancestro más cercano que cumple el selector
closest es especialmente útil con los eventos, como verás en la lección de eventos.
Para qué usar todo esto
Con el DOM puedes hacer casi cualquier cosa con una página, pero no todo lo que se puede hacer se debe hacer. Busca funcionalidad que ayude a la persona: mostrar solo las opciones que tienen sentido en cada momento, dar ayuda contextual, validar un formulario mientras se escribe, mostrar el progreso de una tarea. Evita los efectos llamativos que no aportan nada y los comportamientos que alguien no espera.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.