DOM en JavaScript: nodos, selección y actualización | Nicolás Garzón
DOM
HTML
Copiar < main>
< h1> Products</ h1>
< button> Save</ button>
</ main> Representación conceptual:
Texto
Copiar Document
└─ html
└─ body
└─ main
├─ h1
└─ button
Document: entrada al documento.
Node: base para elementos, texto, comentarios y otros nodos.
Element: nodo que representa una etiqueta.
HTMLElement: elemento HTML con capacidades adicionales.
JavaScript
Copiar const button = document. querySelector ( "button" ) ;
button instanceof Element ;
button instanceof HTMLElement ; JavaScript
Copiar const app = document. getElementById ( "app" ) ;
const button = document. querySelector (
"[data-action='save']" ,
) ;
const items = document. querySelectorAll ( ".item" ) ; querySelector retorna el primer match o null.
querySelectorAll retorna una NodeList estática.
JavaScript
Copiar if ( ! button) {
throw new Error ( "Save button was not found" ) ;
} No uses una aserción mental de que el elemento siempre existe; el documento y el momento de ejecución forman parte del contrato.
JavaScript
Copiar document. querySelectorAll ( ".item" ) ; Devuelve normalmente una colección estática.
JavaScript
Copiar document. getElementsByClassName ( "item" ) ; Devuelve una HTMLCollection viva que refleja cambios posteriores.
Las colecciones vivas pueden cambiar mientras las recorres y producir resultados inesperados.
JavaScript
Copiar element. parentElement;
element. children;
element. firstElementChild;
element. nextElementSibling; JavaScript
Copiar node. parentNode;
node. childNodes;
node. firstChild; children contiene elementos; childNodes también puede incluir texto y comentarios.
JavaScript
Copiar const card = button. closest ( "[data-card]" ) ;
button. matches ( "button.primary" ) ; closest busca desde el elemento hacia sus ancestros. Es especialmente útil en delegación de eventos.
JavaScript
Copiar const item = document. createElement ( "li" ) ;
item. className = "product-item" ;
item. textContent = product. name; JavaScript
Copiar list. append ( item) ;
list. prepend ( item) ;
reference. before ( item) ;
reference. after ( item) ;
reference. replaceWith ( item) ; Un nodo solo puede ocupar un lugar del árbol. Insertarlo en otro sitio lo mueve.
JavaScript
Copiar const fragment = document. createDocumentFragment ( ) ;
for ( const product of products) {
const item = document. createElement ( "li" ) ;
item. textContent = product. name;
fragment. append ( item) ;
}
list. append ( fragment) ; El fragmento permite preparar un grupo sin insertarlo elemento por elemento. No garantiza por sí solo que cualquier código sea rápido; el patrón reduce trabajo observable sobre el documento.
JavaScript
Copiar element. textContent = userInput; Trata el valor como texto. Suele ser la opción correcta para información no confiable.
Refleja texto renderizado y puede necesitar consultar estilos y layout. Su semántica es visual, no solo estructural.
JavaScript
Copiar element. innerHTML = markup; Parsea HTML. Insertar texto no confiable puede producir XSS.
No uses innerHTML únicamente por comodidad. Construye nodos o utiliza una política de sanitización confiable cuando realmente necesitas HTML.
HTML
Copiar < input value = " initial" > JavaScript
Copiar input. getAttribute ( "value" ) ;
input. value; El atributo representa el valor declarado; la propiedad puede reflejar el estado actual del control.
JavaScript
Copiar input. setAttribute ( "disabled" , "" ) ;
input. disabled = true ; Para boolean attributes, la presencia del atributo comunica verdadero; disabled="false" continúa presente.
HTML
Copiar < button data-product-id = " product-1" > </ button> JavaScript
Copiar button. dataset. productId; dataset expone strings. Convierte y valida cuando necesitas números, booleanos u otras estructuras.
JavaScript
Copiar element. classList. add ( "active" ) ;
element. classList. remove ( "active" ) ;
element. classList. toggle ( "active" , isActive) ;
element. classList. contains ( "active" ) ; Cambiar clases suele separar mejor comportamiento y presentación que escribir múltiples estilos inline.
JavaScript
Copiar element. style. width = "200px" ; Solo refleja estilos inline.
JavaScript
Copiar const styles = getComputedStyle ( element) ;
styles. width; Consulta el resultado calculado y puede forzar trabajo de estilo o layout si se combina con escrituras pendientes.
JavaScript
Copiar for ( const item of items) {
item. style. width = ` ${ item. offsetWidth + 10 } px ` ;
} Alternar lecturas geométricas y escrituras puede obligar al navegador a recalcular repetidamente.
Agrupa lecturas y después escrituras cuando el rendimiento sea relevante.
JavaScript
Copiar const rect = element. getBoundingClientRect ( ) ; Entrega coordenadas respecto al viewport en ese momento. Scroll, zoom, fuentes y layout pueden cambiar el resultado.
Para observar visibilidad o tamaño, APIs como IntersectionObserver y ResizeObserver suelen expresar mejor la necesidad que consultar constantemente en scroll.
JavaScript
Copiar const formData = new FormData ( form) ; Los controles tienen propiedades como value, checked, files y validación nativa. No confíes únicamente en el DOM; el servidor debe validar nuevamente.
JavaScript
Copiar element. remove ( ) ; Eliminar un nodo del documento no garantiza que sea recolectado si listeners, closures, arrays u otras referencias todavía lo conservan.
JavaScript
Copiar const observer = new MutationObserver ( ( records ) => {
console. log ( records) ;
} ) ;
observer. observe ( target, {
childList : true ,
subtree : true ,
} ) ; Las callbacks se entregan mediante microtasks. Desconecta cuando ya no necesitas observar.
JavaScript
Copiar observer. disconnect ( ) ; Cambiar el DOM también cambia la experiencia de tecnologías asistivas.
Usa elementos semánticos.
Conserva nombres accesibles.
Gestiona foco cuando el flujo lo necesita.
No reemplaces un botón con un div clickable.
Comunica cambios dinámicos importantes de forma apropiada.
La accesibilidad no se soluciona añadiendo ARIA a una estructura incorrecta.
JavaScript
Copiar function createProductItem ( product ) {
const item = document. createElement ( "li" ) ;
const title = document. createElement ( "h2" ) ;
const price = document. createElement ( "span" ) ;
title. textContent = product. name;
price. textContent = formatPrice ( product. price) ;
item. dataset. productId = product. id;
item. append ( title, price) ;
return item;
} Los datos se insertan como texto y la estructura permanece explícita.
Asumir que querySelector nunca retorna null.
Confundir NodeList y Array.
Mezclar colecciones vivas con mutaciones durante el recorrido.
Insertar entrada no confiable mediante innerHTML.
Confundir atributos con propiedades actuales.
Utilizar disabled="false" esperando habilitar.
Alternar lecturas y escrituras de layout repetidamente.
Eliminar nodos sin limpiar referencias y listeners.
Manipular interfaz sin considerar foco y semántica.
Pensar que el DOM pertenece a JavaScript como lenguaje.
El DOM representa el documento como nodos.
Document es la entrada; Element representa etiquetas.
querySelector puede devolver null.
textContent inserta texto; innerHTML parsea markup.
Atributos y propiedades no siempre representan el mismo estado.
Los cambios de layout tienen costo.
Observers necesitan cleanup.
Un nodo retirado puede seguir en memoria por otras referencias.
La estructura semántica también es parte del comportamiento.
¿Por qué input.getAttribute("value") y input.value pueden devolver valores distintos?
Respuesta El atributo conserva el valor declarado en el markup, mientras que la propiedad refleja el estado actual del control después de interacción o asignaciones.
Eventos, propagación y delegación explica cómo el navegador comunica interacciones y cambios.