Aller au contenu

Traverser le DOM en JavaScript

La traversée du DOM (Document Object Model) est une compétence fondamentale pour les développeurs web utilisant JavaScript. Maîtriser la traversée du DOM vous permettra de manipuler les pages web de manière dynamique, en créant des expériences utilisateur interactives et réactives. Ce guide vous fournira des explications détaillées et de nombreux exemples de code pour vous aider à devenir compétent en traversée du DOM.

Introduction à la traversée du DOM

Le DOM représente la structure d’une page web sous forme d’arbre de nœuds. Chaque nœud correspond à un élément ou à un morceau de contenu de la page. Traverser le DOM consiste à se déplacer entre ces nœuds pour accéder aux éléments ou les manipuler.

Comprendre l’arbre DOM

Avant de plonger dans les méthodes de traversée, il est crucial de comprendre la structure de l’arbre DOM. Voici un document HTML simple pour l’illustrer :


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>

Dans ce document, l’élément <code><body></code> contient un <code><div></code> avec un id de "container", qui contient à son tour un élément <code><p></code> et un <code><ul></code> avec des enfants `` <li>`.

Méthodes de traversée de base

Accéder aux nœuds enfants

Imaginez que vous avez un blog avec plusieurs articles, et que chaque article a des commentaires. Vous voulez compter les commentaires d’un article spécifique.


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>

Ce code sélectionne le <code><div></code> avec la classe "comments" et affiche le nombre d’éléments de commentaire qu’il contient. Remarque : children ne renvoie que les nœuds éléments, tandis que childNodes inclut les nœuds texte et commentaire. Pour une traversée limitée aux éléments, préférez children.

Imaginez que vous avez une liste d’articles dans un panier d’achat et que vous voulez trouver l’élément conteneur d’un article spécifique.


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>

Ce code sélectionne le premier élément <code><li></code> et affiche le nom de balise de son nœud parent. Pour une navigation limitée aux éléments, parentElement est souvent préférable à parentNode, car il ignore les nœuds texte et renvoie null si le parent n’est pas un élément.

Nœuds frères

Imaginez que vous avez une liste de tâches où vous pouvez marquer les tâches comme terminées, puis passer à la tâche suivante..


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>

Ce code fournit une liste de tâches où chaque tâche possède un bouton "Complete Task". Lorsqu’une tâche est marquée comme terminée, son texte est barré et le bouton est désactivé. Il affiche également la description de la tâche suivante. S’il n’y a plus de tâches, il indique qu’il n’y en a plus. De même, previousElementSibling et nextElementSibling ignorent les nœuds texte, ce qui les rend plus sûrs pour une traversée limitée aux éléments que previousSibling et nextSibling.

Techniques avancées de traversée

Trouver des éléments par classe ou par balise

Imaginez que vous créez un tableau de bord qui répertorie tous les utilisateurs et que vous voulez trouver et compter tous les éléments utilisateur.


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>

Ce code compte et affiche le nombre d’éléments ayant la classe user.

Méthodes de sélecteur de requête

Imaginez que vous avez un site d’actualités et que vous voulez mettre en évidence tous les titres.


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>

Ce code sélectionne tous les éléments avec la classe headline, change leur couleur en rouge et affiche le nombre de ces éléments.

Traverser à l’aide de fonctions récursives

Créons un exemple concret de traversée récursive. Nous utiliserons un système de commentaires imbriqués comme exemple, où chaque commentaire peut avoir des réponses.


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>

Ce code représente un système de commentaires imbriqués avec des commentaires et des réponses. La fonction traverseComments parcourt récursivement chaque commentaire et chaque réponse, en affichant leur contenu textuel. La structure imbriquée permet des réponses aux réponses, démontrant un cas d’utilisation réel de la traversée récursive. Incluez toujours une vérification de null/undefined au début des fonctions récursives afin d’éviter les erreurs lorsque le sélecteur initial ne renvoie rien.

Exemples pratiques

Ces exemples combinent la traversée du DOM avec des techniques de manipulation courantes pour illustrer des flux de travail réels.

Créer une liste de tâches dynamique

Imaginez que vous avez une liste de tâches où les utilisateurs peuvent ajouter de nouvelles tâches.


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>

Ce code permet aux utilisateurs d’ajouter de nouvelles tâches à une liste de tâches en saisissant du texte dans un champ de saisie et en cliquant sur un bouton.

Mettre à jour les attributs d’un élément

Imaginez que vous avez une liste de produits et que vous voulez marquer les produits comme "favoris" lorsqu’ils sont cliqués.


html
<!DOCTYPE html>
<html>
<head>
    <title>Updating Element Attributes</title>
    <style>
        .favorite { font-weight: bold; color: gold; }
        .info { color: darkblue; }
    </style>
</head>
<body>
<h4>Click on the list item below to see the result!</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>

Ce code permet aux utilisateurs de marquer des produits comme "favoris" en cliquant dessus, en modifiant leur apparence à l’aide d’une classe favorite.

INFO

Réduisez au minimum l’accès au DOM pour améliorer les performances. Regroupez les manipulations du DOM afin de réduire les reflows et les repaints.

Conclusion

Maîtriser la traversée du DOM est essentiel pour créer des applications web dynamiques et interactives. En comprenant et en utilisant les différentes méthodes et techniques de navigation et de manipulation du DOM, vous pouvez améliorer l’expérience utilisateur et la fonctionnalité de vos projets web.

Practice

Which of the following methods can be used to traverse the DOM in JavaScript?

Trouvez-vous cela utile?

Aperçu dual-run — comparez avec les routes Symfony en production.