Recorrido del DOM en JavaScript
Recorrer el DOM (Modelo de Objetos del Documento) es una habilidad fundamental para los desarrolladores web que usan JavaScript. Dominar el recorrido del DOM te permitirá manipular páginas web de forma dinámica, creando experiencias de usuario interactivas y adaptativas. Esta guía te proporcionará explicaciones detalladas y múltiples ejemplos de código para ayudarte a volverte competente en el recorrido del DOM.
Introducción al recorrido del DOM
El DOM representa la estructura de una página web como un árbol de nodos. Cada nodo corresponde a un elemento o a una parte del contenido de la página. Recorrer el DOM implica moverse entre estos nodos para acceder a elementos o manipularlos.
Entender el árbol del DOM
Antes de profundizar en los métodos de recorrido, es crucial entender la estructura del árbol del DOM. Aquí tienes un documento HTML sencillo para ilustrarlo:
<!DOCTYPE html>
<html>
<head>
<title>DOM Traversal Example</title>
</head>
<body>
<div id="container">
<p class="text">Hello, World!</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</body>
</html>En este documento, el elemento <code><body></code> contiene un <code><div></code> con un id de "container", que a su vez contiene un elemento <code><p></code> y un <code><ul></code> con hijos `` <li>`.
Métodos básicos de recorrido
Acceder a nodos hijos
Imagina que tienes un blog con varias entradas y que cada entrada tiene comentarios. Quieres contar los comentarios de una entrada específica.
<!DOCTYPE html>
<html>
<head>
<title>Accessing Child Nodes</title>
</head>
<body>
<div id="blog-post">
<h2>Blog Post Title</h2>
<p>Some interesting content...</p>
<div class="comments">
<p>Comment 1</p>
<p>Comment 2</p>
<p>Comment 3</p>
</div>
</div>
<script>
const commentsContainer = document.querySelector('.comments');
const comments = commentsContainer.children; // Only includes element nodes
// Display the number of comments
console.log(`Number of comments: ${comments.length}`);
</script>
</body>
</html>Este código selecciona el <code><div></code> con la clase "comments" y muestra el número de elementos de comentario dentro de él. Nota: children devuelve solo nodos de elemento, mientras que childNodes incluye nodos de texto y de comentario. Para recorrer solo elementos, es preferible children.
Navegar a nodos padre
Imagina que tienes una lista de elementos en un carrito de compras y quieres encontrar el elemento contenedor de un elemento específico.
<!DOCTYPE html>
<html>
<head>
<title>Navigating to Parent Nodes</title>
</head>
<body>
<div id="shopping-cart">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
const cartItem = document.querySelector('li');
const parent = cartItem.parentNode;
// Display the parent node
console.log(`The parent of the first cart item is a: ${parent.tagName}`);
</script>
</body>
</html>Este código selecciona el primer elemento <code><li></code> y muestra el nombre de etiqueta de su nodo padre. Para la navegación solo entre elementos, a menudo se prefiere parentElement en lugar de parentNode, ya que omite los nodos de texto y devuelve null si el padre no es un elemento.
Nodos hermanos
Imagina que tienes una lista de tareas en la que puedes marcar tareas como completadas y luego pasar a la siguiente tarea..
<!DOCTYPE html>
<html>
<head>
<title>Task List Navigation</title>
<style>
.task {
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
}
.completed {
text-decoration: line-through;
color: gray;
}
</style>
</head>
<body>
<div class="task-list">
<div class="task">
<p>Task 1: Do the laundry</p>
<button class="complete-task">Complete Task</button>
</div>
<div class="task">
<p>Task 2: Buy groceries</p>
<button class="complete-task">Complete Task</button>
</div>
<div class="task">
<p>Task 3: Clean the house</p>
<button class="complete-task">Complete Task</button>
</div>
</div>
<script>
document.querySelectorAll('.complete-task').forEach(button => {
button.addEventListener('click', () => {
const task = button.parentElement;
task.classList.add('completed');
button.disabled = true;
const nextTask = task.nextElementSibling;
if (nextTask) {
console.log(`Next task: ${nextTask.querySelector('p').textContent}`);
} else {
console.log('No more tasks available');
}
});
});
</script>
</body>
</html>Este código proporciona una lista de tareas en la que cada tarea tiene un botón "Complete Task". Cuando una tarea se marca como completada, se tacha el texto y se deshabilita el botón. También muestra la descripción de la siguiente tarea. Si no hay más tareas, indica que no hay más tareas disponibles. Del mismo modo, previousElementSibling y nextElementSibling omiten los nodos de texto, lo que los hace más seguros para el recorrido solo entre elementos que previousSibling y nextSibling.
Técnicas avanzadas de recorrido
Encontrar elementos por clase o etiqueta
Imagina que estás creando un panel que enumera a todos los usuarios y quieres encontrar y contar todos los elementos de usuario.
<!DOCTYPE html>
<html>
<head>
<title>Finding Elements by Class or Tag</title>
</head>
<body>
<div class="user">User 1</div>
<div class="user">User 2</div>
<div class="user">User 3</div>
<script>
const users = document.getElementsByClassName('user');
// Display the number of users
console.log(`Number of users: ${users.length}`);
</script>
</body>
</html>Este código cuenta y muestra el número de elementos con la clase user.
Métodos de selector de consulta
Imagina que tienes un sitio de noticias y quieres resaltar todos los titulares.
<!DOCTYPE html>
<html>
<head>
<title>Query Selector Methods</title>
</head>
<body>
<div id="news">
<h1 class="headline">Headline 1</h1>
<h1 class="headline">Headline 2</h1>
<h1 class="headline">Headline 3</h1>
</div>
<script>
const headlines = document.querySelectorAll('.headline');
// Highlight all headlines
headlines.forEach(headline => {
headline.style.color = 'red';
});
// Display the number of headlines
console.log(`Number of headlines: ${headlines.length}`);
</script>
</body>
</html>Este código selecciona todos los elementos con la clase headline, cambia su color a rojo y muestra el recuento de estos elementos.
Recorrer usando funciones recursivas
Vamos a crear un ejemplo real de recorrido recursivo. Usaremos como ejemplo un sistema de comentarios anidados, donde cada comentario puede tener respuestas.
<!DOCTYPE html>
<html>
<head>
<title>Recursive Traversal</title>
<style>
.comment {
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
}
.reply {
margin-left: 20px;
border-left: 2px solid #aaa;
}
</style>
</head>
<body>
<div class="comments">
<div class="comment">
<p>Comment 1</p>
<div class="reply">
<p>Reply 1-1</p>
<div class="reply">
<p>Reply 1-1-1</p>
</div>
</div>
<div class="reply">
<p>Reply 1-2</p>
</div>
</div>
<div class="comment">
<p>Comment 2</p>
<div class="reply">
<p>Reply 2-1</p>
</div>
</div>
</div>
<script>
function traverseComments(node) {
if (!node) return; // Guard against null/undefined
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('comment')) {
console.log(`Comment: ${node.querySelector('p').textContent}`);
}
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('reply')) {
console.log(`Reply: ${node.querySelector('p').textContent}`);
}
for (let i = 0; i < node.childNodes.length; i++) {
traverseComments(node.childNodes[i]);
}
}
traverseComments(document.querySelector('.comments'));
</script>
</body>
</html>Este código representa un sistema de comentarios anidados con comentarios y respuestas. La función traverseComments recorre recursivamente cada comentario y respuesta, mostrando su contenido de texto. La estructura anidada permite respuestas a respuestas, demostrando un caso de uso real del recorrido recursivo. Incluye siempre una comprobación de null/undefined al inicio de las funciones recursivas para evitar errores cuando el selector inicial no devuelve nada.
Ejemplos prácticos
Estos ejemplos combinan el recorrido del DOM con técnicas comunes de manipulación para demostrar flujos de trabajo reales.
Crear una lista dinámica de tareas pendientes
Imagina que tienes una lista de tareas pendientes en la que los usuarios pueden añadir nuevas tareas.
<!DOCTYPE html>
<html>
<head>
<title>Dynamic To-Do List</title>
<style>
.info { color: darkgreen; }
</style>
</head>
<body>
<div id="todo-list">
<h2>To-Do List</h2>
<ul id="tasks">
<li>Task 1</li>
<li>Task 2</li>
</ul>
<input type="text" id="task-input" placeholder="Add a new task" />
<button id="add-button">Add Task</button>
</div>
<script>
const tasks = document.getElementById('tasks');
const input = document.getElementById('task-input');
const button = document.getElementById('add-button');
button.addEventListener('click', () => {
const newTask = input.value.trim();
if (newTask) {
const li = document.createElement('li');
li.textContent = newTask;
tasks.appendChild(li);
input.value = '';
console.log('Added new task to the to-do list');
}
});
</script>
</body>
</html>Este código permite a los usuarios añadir nuevas tareas a una lista de tareas pendientes introduciendo texto en un campo de entrada y haciendo clic en un botón.
Actualizar atributos de elementos
Imagina que tienes una lista de productos y quieres marcar los productos como "favoritos" cuando se hace clic en ellos.
<!DOCTYPE html>
<html>
<head>
<title>Updating Element Attributes</title>
<style>
.favorite { font-weight: bold; color: gold; }
.info { color: darkblue; }
</style>
</head>
<body>
<h4>¡Haz clic en el elemento de la lista de abajo para ver el resultado!</h4>
<ul id="product-list">
<li>Product 1</li>
<li>Product 2</li>
<li>Product 3</li>
</ul>
<script>
const productList = document.getElementById('product-list');
productList.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
event.target.classList.toggle('favorite');
console.log(`Toggled favorite status for: ${event.target.textContent}`);
}
});
</script>
</body>
</html>Este código permite a los usuarios marcar productos como "favoritos" al hacer clic en ellos, cambiando su apariencia mediante una clase favorite.
INFO
Minimiza el acceso al DOM para mejorar el rendimiento. Agrupa las manipulaciones del DOM para reducir los reflows y repaints.
Conclusión
Dominar el recorrido del DOM es esencial para crear aplicaciones web dinámicas e interactivas. Al comprender y utilizar los diversos métodos y técnicas para navegar y manipular el DOM, puedes mejorar la experiencia del usuario y la funcionalidad de tus proyectos web.
Practice
Which of the following methods can be used to traverse the DOM in JavaScript?