Saltar al contenido

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:


html
<!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.


html
<!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.

Imagina que tienes una lista de elementos en un carrito de compras y quieres encontrar el elemento contenedor de un elemento específico.


html
<!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..


html
<!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.


html
<!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.


html
<!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.


html
<!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.


html
<!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.


html
<!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?

¿Te resulta útil?

Vista previa dual-run — compárala con las rutas Symfony en producción.