Saltar al contenido principal
Logo de JSCamp

Manipulación del DOM

Aprende a manipular elementos HTML, cambiar contenido, estilos y trabajar con múltiples elementos

midudev

Más clases y directos en YouTube

Suscribirse

Manipulación del DOM

Ya sabemos escuchar eventos, pero ahora vamos a aprender a manipular elementos: cambiar su contenido, estilos, propiedades y trabajar con múltiples elementos a la vez.

Los callbacks se ejecutan cuando ocurre el evento

Es importante entender que el código dentro del addEventListener no se ejecuta inmediatamente. Solo se ejecuta cuando ocurre el evento:

console.log('1. Inicio del script')const boton = document.querySelector('#boton-importante')boton.addEventListener('click', function () {  console.log('3. ¡Click en el botón!')})console.log('2. Fin del script')

Orden de ejecución:

1. Inicio del script2. Fin del script(esperando...)3. ¡Click en el botón!  ← Solo cuando hagas click

¿Qué es un callback?

Un callback es una función que se pasa como parámetro a otra función y se ejecuta más tarde:

// La función anónima es el callbackboton.addEventListener('click', function () {  console.log('Este código solo se ejecuta al hacer click')})// El resto del código se ejecuta normalmenteconsole.log('Este código se ejecuta inmediatamente')

Ejemplo completo:

<button id="boton-importante">Haz clic</button>
console.log('Inicio: Cargando el script')const boton = document.querySelector('#boton-importante')console.log('Botón encontrado:', boton)boton.addEventListener('click', function () {  console.log('¡Hiciste click!')})console.log('Fin: Script cargado, esperando eventos')

Salida en consola:

Inicio: Cargando el scriptBotón encontrado: <button id="boton-importante">Fin: Script cargado, esperando eventos(usuario hace click)¡Hiciste click!

Propiedades y métodos de los elementos

Cuando recuperamos un elemento con querySelector, obtenemos acceso a muchas propiedades y métodos útiles:

const boton = document.querySelector('#boton-importante')// ¿Qué es boton?console.log(boton) // <button id="boton-importante">Haz clic</button>

textContent - Obtener o cambiar el texto

const boton = document.querySelector('#boton-importante')// Leer el contenido de textoconsole.log(boton.textContent) // "Haz clic"// Cambiar el contenido de textoboton.textContent = 'Nuevo texto'

getBoundingClientRect() - Obtener posición y tamaño

const boton = document.querySelector('#boton-importante')const rect = boton.getBoundingClientRect()console.log(rect)/* Salida: {  width: 120,  height: 40,  x: 100,  y: 200,  top: 200,  left: 100,  right: 220,  bottom: 240} */

Otras propiedades útiles:

const boton = document.querySelector('#boton-importante')// innerHTML - Contenido HTML internoconsole.log(boton.innerHTML)// id - El identificadorconsole.log(boton.id) // "boton-importante"// className - Las clases CSSconsole.log(boton.className)// classList - Manipular clases (más adelante)console.log(boton.classList)// disabled - Si está deshabilitadoconsole.log(boton.disabled) // false// value - Valor (para inputs)const input = document.querySelector('#mi-input')console.log(input.value)

Cambiar el contenido de texto con textContent

Vamos a cambiar el texto del botón cuando hacemos click:

<button id="boton-importante">Aplicar a empleo</button>
const boton = document.querySelector('#boton-importante')boton.addEventListener('click', function () {  // Cambiar el texto del botón  boton.textContent = '¡Aplicado!'})

Resultado:

  • Antes del click: Aplicar a empleo
  • Después del click: ¡Aplicado!

Nota sobre accesibilidad: Cuando cambias el texto de un botón dinámicamente, los lectores de pantalla generalmente detectan el cambio automáticamente. Sin embargo, si el cambio es más complejo o quieres asegurar que se anuncie claramente, puedes usar aria-live en un elemento contenedor o considerar usar aria-label para proporcionar contexto adicional sobre el cambio de estado.

Ejemplo más completo:

const boton = document.querySelector('#boton-importante')let aplicado = falseboton.addEventListener('click', function () {  if (aplicado) {    boton.textContent = 'Aplicar a empleo'    aplicado = false  } else {    boton.textContent = '¡Aplicado!'    aplicado = true  }})

Cambiar estilos con la propiedad style

Podemos cambiar los estilos CSS directamente desde JavaScript:

const boton = document.querySelector('#boton-importante')boton.addEventListener('click', function () {  boton.textContent = '¡Aplicado!'  // Cambiar el color de fondo  boton.style.backgroundColor = '#22c55e' // Verde})

⚠️ Importante: Nombres de propiedades CSS

En JavaScript, las propiedades CSS con guiones se escriben en camelCase:

CSS JavaScript
background-color backgroundColor
font-size fontSize
border-radius borderRadius
margin-top marginTop
padding-left paddingLeft

Ejemplo con múltiples estilos:

const boton = document.querySelector('#boton-importante')boton.addEventListener('click', function () {  boton.textContent = '¡Aplicado!'  boton.style.backgroundColor = '#22c55e'  boton.style.color = 'white'  boton.style.border = 'none'  boton.style.cursor = 'not-allowed'})

Cambiar propiedades: disabled

Podemos cambiar propiedades del elemento, como disabled:

const boton = document.querySelector('#boton-importante')boton.addEventListener('click', function () {  boton.textContent = '¡Aplicado!'  boton.style.backgroundColor = '#22c55e'  // Deshabilitar el botón para que no se pueda volver a hacer click  boton.disabled = true})

¿Qué hace disabled?

Cuando un botón tiene disabled = true:

  • ❌ No se puede hacer click
  • ❌ No recibe eventos del teclado
  • 🎨 Cambia la apariencia (suele verse más opaco)
  • 🖱️ El cursor cambia (generalmente a not-allowed)

Trabajar con múltiples elementos: Classes

Hasta ahora hemos trabajado con un solo botón usando id. Pero, ¿qué pasa si tenemos varios botones?

El problema con IDs:

<!-- ❌ No podemos tener múltiples elementos con el mismo ID --><button id="boton-aplicar">Aplicar</button><button id="boton-aplicar">Aplicar</button><button id="boton-aplicar">Aplicar</button>

La solución: Usar clases

<!-- ✅ Podemos tener múltiples elementos con la misma clase --><button class="button-apply-job">Aplicar</button><button class="button-apply-job">Aplicar</button><button class="button-apply-job">Aplicar</button>

querySelector vs querySelectorAll

querySelector - Solo el primero

// Selecciona solo el PRIMER botón que encuentreconst boton = document.querySelector('.button-apply-job')console.log(boton) // <button class="button-apply-job">Aplicar</button>

Si hay múltiples elementos con esa clase, solo devuelve el primero.

querySelectorAll - Todos los elementos

// Selecciona TODOS los botones con esa claseconst botones = document.querySelectorAll('.button-apply-job')console.log(botones) // NodeList(3) [button, button, button]console.log(botones.length) // 3

Devuelve un NodeList con todos los elementos que coinciden.

¿Qué es un NodeList?

Un NodeList es una colección de elementos del DOM. Es similar a un array, pero no es exactamente lo mismo:

const botones = document.querySelectorAll('.button-apply-job')// Tiene .length como los arraysconsole.log(botones.length) // 3// Podemos acceder por índiceconsole.log(botones[0]) // Primer botónconsole.log(botones[1]) // Segundo botónconsole.log(botones[2]) // Tercer botón// Tiene forEach (pero no map, filter, reduce, etc.)botones.forEach(function (boton) {  console.log(boton)})

Usar forEach con el NodeList

Para añadir eventos a todos los botones, usamos forEach:

<button class="button-apply-job">Aplicar - Frontend</button><button class="button-apply-job">Aplicar - Backend</button><button class="button-apply-job">Aplicar - Fullstack</button>
// Seleccionar todos los botonesconst botones = document.querySelectorAll('.button-apply-job')// Añadir evento a cada unobotones.forEach(function (boton) {  boton.addEventListener('click', function () {    console.log('Click en:', boton.textContent)    // Cambiar este botón específico    boton.textContent = '¡Aplicado!'    boton.style.backgroundColor = '#22c55e'    boton.disabled = true  })})

¿Cómo funciona?

  1. querySelectorAll devuelve un NodeList con todos los botones
  2. forEach itera sobre cada botón
  3. Para cada botón, añadimos un addEventListener
  4. Cuando hacemos click en cualquier botón, solo ese botón cambia

Diferencias clave: querySelector vs querySelectorAll

querySelector querySelectorAll
Devuelve un solo elemento Devuelve un NodeList
Si hay múltiples, devuelve el primero Devuelve todos los elementos
Si no encuentra, devuelve null Si no encuentra, devuelve NodeList vacío
Puedes usar .addEventListener directamente Necesitas forEach para cada elemento

Ejemplos:

// querySelector - Solo el primeroconst primerBoton = document.querySelector('.button-apply-job')primerBoton.addEventListener('click', function () {  console.log('Solo funciona en el primer botón')})// querySelectorAll - Todosconst todosLosBotones = document.querySelectorAll('.button-apply-job')todosLosBotones.forEach(function (boton) {  boton.addEventListener('click', function () {    console.log('Funciona en todos los botones')  })})

Arrow functions en forEach

Podemos usar arrow functions para código más limpio:

// Función tradicionalbotones.forEach(function (boton) {  boton.addEventListener('click', function () {    boton.textContent = '¡Aplicado!'  })})// Arrow functionbotones.forEach((boton) => {  boton.addEventListener('click', () => {    boton.textContent = '¡Aplicado!'  })})

Errores comunes

1. Intentar usar addEventListener en un NodeList

// ❌ MAL: No puedes añadir eventos directamente al NodeListconst botones = document.querySelectorAll('.button-apply-job')botones.addEventListener('click', function () {  /* ... */})// Error: botones.addEventListener is not a function// ✅ BIEN: Usa forEachbotones.forEach(function (boton) {  boton.addEventListener('click', function () {    /* ... */  })})

2. Olvidar el punto en el selector de clase

// ❌ MAL: Busca una etiqueta <button-apply-job>const botones = document.querySelectorAll('button-apply-job')// ✅ BIEN: Busca elementos con class="button-apply-job"const botones = document.querySelectorAll('.button-apply-job')

Siguiente paso: Vamos a entender el concepto de event bubbling y cómo manejar eventos en elementos padres para simplificar nuestro código.