El DOM

Hasta ahora tus programas escribían en la consola o dibujaban en un <canvas>.

El DOM es el árbol de la página en memoria; document es la puerta de entrada.

querySelector (uno) y querySelectorAll (todos) con selectores de CSS.

textContent para textos; classList para cambiar el aspecto; setProperty para variables CSS.

data-* y dataset para datos propios; createElement y append para generar contenido a partir de datos.

  • Hasta ahora tus programas escribían en la consola o dibujaban en un <canvas>. Ahora vas a hacer que JavaScript cambie la propia página: textos, clases, estilos y elementos nuevos. Para eso se usa el DOM.
  • Cuando el navegador lee tu HTML, construye en memoria un árbol de objetos: el DOM (Document Object Model). Cada etiqueta es un nodo con padres e hijos:
  • document └── html ├── head │ └── title └── body ├── header │ └── h1 "Estudio Norte" └── main ├── p "Diseño gráfico e interactivo" └── ul ├── li "Branding" └── li "Web"
  • JavaScript accede a ese árbol con el objeto document. Si cambias el DOM, la página cambia al instante, sin recargar. Tu fichero index.html no se modifica: lo que cambia es la copia en memoria.
  • Puedes verlo en las DevTools, pestaña Elementos: lo que ves ahí es el DOM, no el HTML original.

Introducción

Hasta ahora tus programas escribían en la consola o dibujaban en un <canvas>. Ahora vas a hacer que JavaScript cambie la propia página: textos, clases, estilos y elementos nuevos. Para eso se usa el DOM.

Qué es el DOM

Cuando el navegador lee tu HTML, construye en memoria un árbol de objetos: el DOM (Document Object Model). Cada etiqueta es un nodo con padres e hijos:

document └── html ├── head │ └── title └── body ├── header │ └── h1 "Estudio Norte" └── main ├── p "Diseño gráfico e interactivo" └── ul ├── li "Branding" └── li "Web"

JavaScript accede a ese árbol con el objeto document. Si cambias el DOM, la página cambia al instante, sin recargar. Tu fichero index.html no se modifica: lo que cambia es la copia en memoria.

Puedes verlo en las DevTools, pestaña Elementos: lo que ves ahí es el DOM, no el HTML original.

Seleccionar elementos

Antes de cambiar algo hay que encontrarlo. Se usan los mismos selectores de CSS:

const titulo = document.querySelector("h1"); // el primero que coincida const menu = document.querySelector("#menu"); // por id const destacado = document.querySelector(".destacado"); // por clase const tarjetas = document.querySelectorAll(".tarjeta"); // todos: una lista

querySelector devuelve el primer elemento que coincide, o null si no hay ninguno. querySelectorAll devuelve todos en una lista (NodeList) que se recorre con forEach:

tarjetas.forEach((tarjeta) => { console.log(tarjeta.textContent); });

Cambiar el texto

const titulo = document.querySelector("h1"); titulo.textContent = "Estudio Norte · Portfolio 2027";

textContent lee o cambia el texto. Es la opción segura: lo que escribas se muestra tal cual, aunque contenga < o >. innerHTML interpreta el texto como HTML. Úsalo solo con contenido que controlas tú, nunca con lo que escribe una persona en un formulario: podría colar código (inyección de HTML).

Cambiar clases: la forma recomendada de cambiar el aspecto

La mejor manera de cambiar el aspecto desde JavaScript es añadir o quitar clases y dejar el diseño en el CSS:

.oculto { display: none; } .activo { background: var(--color-marca); color: white; }

const aviso = document.querySelector(".aviso"); aviso.classList.add("oculto"); // añade la clase aviso.classList.remove("oculto"); // la quita aviso.classList.toggle("activo"); // la pone si no está; la quita si está aviso.classList.contains("activo"); // true o false

Así el diseño sigue en su sitio (el CSS) y JavaScript solo decide cuándo cambia.

Cambiar estilos y variables CSS

Puedes cambiar un estilo concreto con style, pero úsalo para valores que se calculan (una posición, un color elegido por el usuario):

aviso.style.backgroundColor = "hsl(210 70% 45%)"; // propiedades en camelCase

Mejor aún: cambiar una variable CSS (lección El color en CSS). Todos los elementos que la usen se actualizan a la vez:

document.documentElement.style.setProperty("--color-marca", "#e63946");

Atributos y datos propios

const foto = document.querySelector(".foto"); foto.setAttribute("alt", "Cartel del festival en tonos rojos"); foto.getAttribute("src");

Los atributos data-* sirven para guardar datos propios en el HTML. Se leen con dataset:

<article class="tarjeta" data-categoria="branding" data-anio="2026">…</article>

const tarjeta = document.querySelector(".tarjeta"); tarjeta.dataset.categoria; // "branding" tarjeta.dataset.anio; // "2026" (siempre texto)

Crear elementos a partir de datos

Muchas webs no escriben cada tarjeta a mano: las generan a partir de datos (un array de objetos, como en la lección Arrays y objetos). Se crea el elemento, se rellena y se añade:

const proyectos = [ { titulo: "Identidad Café Lento", categoria: "branding" }, { titulo: "Web del festival", categoria: "web" }, ]; const lista = document.querySelector("#proyectos"); proyectos.forEach((p) => { const item = document.createElement("li"); item.textContent = p.titulo; item.dataset.categoria = p.categoria; lista.append(item); });

document.createElement("li") crea el elemento (todavía fuera de la página). append lo añade al final del elemento padre. prepend lo añade al principio. elemento.remove() lo elimina.

Dónde poner el script

Como hasta ahora, enlaza el script en el <head> con defer: se ejecuta cuando el HTML ya está completo y todos los elementos existen.

<script src="js/main.js" defer></script>

Si seleccionas un elemento antes de que exista, obtienes null y el error típico: Cannot read properties of null.

Practica

En una página con un h1, un párrafo y una lista vacía <ul id="colores"></ul>:

Cambia el texto del h1 con textContent. Añade al párrafo la clase destacado (defínela en el CSS). Con un array ["#264653", "#2a9d8f", "#e9c46a"], crea un li por color con el código como texto y ese color de fondo (style.backgroundColor). Cambia la variable --color-marca con setProperty y comprueba que todo lo que la usa cambia.

Errores frecuentes

null al seleccionar: el selector está mal escrito (falta el . o el #) o el script se ejecuta antes que el HTML (falta defer). Tratar el resultado de querySelectorAll como un solo elemento: tarjetas.classList no existe; hay que recorrerlo con forEach. Usar innerHTML con textos que escribe el usuario. Cambiar muchos style uno a uno en lugar de una clase. Olvidar que dataset devuelve texto: Number(tarjeta.dataset.anio).

Código del tema: classList.toggle()

📘 Teoría

Introducción

Hasta ahora tus programas escribían en la consola o dibujaban en un <canvas>. Ahora vas a hacer que JavaScript cambie la propia página: textos, clases, estilos y elementos nuevos. Para eso se usa el DOM.

Qué es el DOM

Cuando el navegador lee tu HTML, construye en memoria un árbol de objetos: el DOM (Document Object Model). Cada etiqueta es un nodo con padres e hijos:

document
└── html
    ├── head
    │   └── title
    └── body
        ├── header
        │   └── h1  "Estudio Norte"
        └── main
            ├── p   "Diseño gráfico e interactivo"
            └── ul
                ├── li  "Branding"
                └── li  "Web"

JavaScript accede a ese árbol con el objeto document. Si cambias el DOM, la página cambia al instante, sin recargar. Tu fichero index.html no se modifica: lo que cambia es la copia en memoria.

Puedes verlo en las DevTools, pestaña Elementos: lo que ves ahí es el DOM, no el HTML original.

Seleccionar elementos

Antes de cambiar algo hay que encontrarlo. Se usan los mismos selectores de CSS:

const titulo = document.querySelector("h1");            // el primero que coincida
const menu = document.querySelector("#menu");           // por id
const destacado = document.querySelector(".destacado"); // por clase
const tarjetas = document.querySelectorAll(".tarjeta"); // todos: una lista
  • querySelector devuelve el primer elemento que coincide, o null si no hay ninguno.
  • querySelectorAll devuelve todos en una lista (NodeList) que se recorre con forEach:
tarjetas.forEach((tarjeta) => {
  console.log(tarjeta.textContent);
});

💡 Consejo. Guarda en constantes los elementos que vas a usar varias veces y selecciónalos al principio del fichero. El código queda más claro y el navegador no busca lo mismo una y otra vez.

Cambiar el texto

const titulo = document.querySelector("h1");
titulo.textContent = "Estudio Norte · Portfolio 2027";
  • textContent lee o cambia el texto. Es la opción segura: lo que escribas se muestra tal cual, aunque contenga < o >.
  • innerHTML interpreta el texto como HTML. Úsalo solo con contenido que controlas tú, nunca con lo que escribe una persona en un formulario: podría colar código (inyección de HTML).

Cambiar clases: la forma recomendada de cambiar el aspecto

La mejor manera de cambiar el aspecto desde JavaScript es añadir o quitar clases y dejar el diseño en el CSS:

.oculto { display: none; }
.activo { background: var(--color-marca); color: white; }
const aviso = document.querySelector(".aviso");
aviso.classList.add("oculto");       // añade la clase
aviso.classList.remove("oculto");    // la quita
aviso.classList.toggle("activo");    // la pone si no está; la quita si está
aviso.classList.contains("activo");  // true o false

Así el diseño sigue en su sitio (el CSS) y JavaScript solo decide cuándo cambia.

Cambiar estilos y variables CSS

Puedes cambiar un estilo concreto con style, pero úsalo para valores que se calculan (una posición, un color elegido por el usuario):

aviso.style.backgroundColor = "hsl(210 70% 45%)";   // propiedades en camelCase

Mejor aún: cambiar una variable CSS (lección El color en CSS). Todos los elementos que la usen se actualizan a la vez:

document.documentElement.style.setProperty("--color-marca", "#e63946");

(document.documentElement es el elemento <html>, donde vive :root.)

Atributos y datos propios

const foto = document.querySelector(".foto");
foto.setAttribute("alt", "Cartel del festival en tonos rojos");
foto.getAttribute("src");

Los atributos data-* sirven para guardar datos propios en el HTML. Se leen con dataset:

<article class="tarjeta" data-categoria="branding" data-anio="2026">…</article>
const tarjeta = document.querySelector(".tarjeta");
tarjeta.dataset.categoria;   // "branding"
tarjeta.dataset.anio;        // "2026" (siempre texto)

Sirven, por ejemplo, para filtrar proyectos por categoría (lección Eventos).

Crear elementos a partir de datos

Muchas webs no escriben cada tarjeta a mano: las generan a partir de datos (un array de objetos, como en la lección Arrays y objetos). Se crea el elemento, se rellena y se añade:

const proyectos = [
  { titulo: "Identidad Café Lento", categoria: "branding" },
  { titulo: "Web del festival", categoria: "web" },
];

const lista = document.querySelector("#proyectos");

proyectos.forEach((p) => {
  const item = document.createElement("li");
  item.textContent = p.titulo;
  item.dataset.categoria = p.categoria;
  lista.append(item);
});
  • document.createElement("li") crea el elemento (todavía fuera de la página).
  • append lo añade al final del elemento padre. prepend lo añade al principio.
  • elemento.remove() lo elimina.

Dónde poner el script

1

Como hasta ahora, enlaza el script en el <head> con defer: se ejecuta cuando el HTML ya está completo y todos los elementos existen.

2

<script src="js/main.js" defer></script>

3

Si seleccionas un elemento antes de que exista, obtienes null y el error típico: Cannot read properties of null.

Practica

En una página con un h1, un párrafo y una lista vacía <ul id="colores"></ul>:

  1. Cambia el texto del h1 con textContent.
  2. Añade al párrafo la clase destacado (defínela en el CSS).
  3. Con un array ["#264653", "#2a9d8f", "#e9c46a"], crea un li por color con el código como texto y ese color de fondo (style.backgroundColor).
  4. Cambia la variable --color-marca con setProperty y comprueba que todo lo que la usa cambia.

Errores frecuentes

  • null al seleccionar: el selector está mal escrito (falta el . o el #) o el script se ejecuta antes que el HTML (falta defer).
  • Tratar el resultado de querySelectorAll como un solo elemento: tarjetas.classList no existe; hay que recorrerlo con forEach.
  • Usar innerHTML con textos que escribe el usuario.
  • Cambiar muchos style uno a uno en lugar de una clase.
  • Olvidar que dataset devuelve texto: Number(tarjeta.dataset.anio).

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros. culTest

La hice en febrero de 2026 para facilitar el aprendizaje de mis alumnos. La idea es aprender desarrollo web practicando y que el proyecto siga creciendo con nuevos temas, tests y retos.

Está inspirada en MDN, W3Schools, CodePen, Manz y muchos otros sitios de documentación sobre desarrollo web. Quería combinar teoría útil, ejemplos ejecutables, retos y el sistema de tests que ya tenía en culTest. culTest

Si te gustó, si no te gustó o si quieres escribirme, puedes hacerlo en cristianeslava@gmail.com