Formation JS | Manipuler le DOM
Le DOM est une interface de programmation permettant d'accéder et de manipuler la structure HTML d'une page web en JavaScript.
Qu’est-ce que le DOM ?
Le document HTML chargé dans le navigateur (= la page web) et tout son contenu (= balises + textes) est accessible au moyen du Modèle Objet du Document ou DOM (Document Objet Model).
Le DOM est une structure arborescente (🌳 un arbre) créée par le navigateur permettant d’accéder et de manipuler (ajouter, modifier ou supprimer des nœuds) la structure HTML d’une page web en JavaScript, après son chargement.
On parle aussi du DOM comme d’une interface de programmation qui représente les éléments HTML comme des objets avec des propriétés et des méthodes.
Un peu de vocabulaire…
Dans la structure d’une page web HTML, tous les éléments (balises et textes) sont des nœuds (nodes) descendants des objets window (fenêtre du navigateur) et document (page web).
racine
(root) : le nœud de plus haut niveau (balise<html>
).enfant
(child) : un nœud directement à l’intérieur d’un autre nœud.descendant
(descendent) : un nœud n’importe où à l’intérieur d’un autre nœud.parent
(parent) : un nœud qui a un autre nœud directement à l’intérieur - inverse d’un enfant.ancêtre
(ancestor) : un nœud qui a un autre nœud n’importe où à l’intérieur - inverse de descendant.frères
(sibling) : des nœuds qui ont le même parent.
<html>
<head>
<meta charset="utf-8">
<title>Apprendre le développement web - laConsole</title>
</head>
<body>
<header>
<nav>
<a href="https://laconsole.dev">Accueil</a>
<a href="https://laconsole.dev/formations">Formations</a>
<a href="https://laconsole.dev/blog">Le blog</a>
</nav>
</header>
<main>
<p>Lorem ipsum...</p>
</main>
</body>
</html>
Dans cet exemple :
<html>
est le noeud racine<meta>
ettitle
sont les enfants de<head>
<body>
est le parent deheader
et<main>
<a>
dans<nav>
sont frères<body>
est l’ancêtre de<header>
<nav>
et<a>
Accéder au DOM avec les sélecteurs
Un certain nombre de méthodes nous permettent de faire des sélections précises d’éléments de notre page web. Ces sélections se font en suivant la syntaxe element.method()
, method
étant le nom de la fonction de sélection à utiliser. Ces méthodes de sélection sont découpées en 2 grandes familles :
- Les sélecteurs traditionnels : ciblant des balises par leur nom ou des attributs spécifiques comme l’
id
et laclass
- Les sélecteurs CSS : ciblant des balises en suivant la syntaxe des sélecteurs CSS
Elles fonctionnent toutes de manière similaire et renvoient dans une variable un ou plusieurs éléments correspondant à la sélection effectuée.
Sélecteurs traditionnels
Sélection par balise (tag)
Il est possible de sélectionner des éléments par nom de balise (tag) avec l’instruction getElementsByTagName()
. Cette méthode retourne les objets au sein d’un tableau JavaScript.
<header>
<nav>...</nav>
</header>
<main>
<nav>...</nav>
</main>
// Retourne un tableau d'objets correspondant à toutes les balises "nav" de la page
document.getElementsByTagName("nav");
Sélection par id
Il est possible de sélectionner un élément par son id
avec l’instruction getElementById()
.
<p id="welcome-message">...</p>
// Retourne un objet correspondant à balise qui a l'id "welcome-message".
document.getElementById("welcome-message");
Sélection par class
Il est possible de sélectionner plusieurs éléments par leur class
avec l’instruction getElementsByClassName()
. Cette méthode retourne les objets au sein d’un tableau JavaScript.
<article class="product">...</article>
<article class="product">...</article>
<article class="product">...</article>
// Retourne un tableau d'objets correspondant aux 3 balises portant la classe "product".
document.getElementsByClassName("product");
Sélecteurs CSS
Il est aussi possible de sélectionner des éléments HTML via des sélecteurs CSS, beaucoup plus précis et détaillés que les balises ou attributs id
et class
.
Pour cela, on fait appel aux méthodes querySelector()
et querySelectorAll()
.
Sélection unique
querySelector()
: retourne la première occurrence répondant au sélecteur.
document.querySelector("p"); // Retourne un objet correspondant au 1er "p"
document.querySelector("#logo"); // Retourne un objet correspondant à l'image qui a l'id "logo"
Sélection multiple
querySelectorAll()
: retourne toutes les occurrences répondant au sélecteur. Cette méthode retourne les objets au sein d’un tableau.
document.querySelectorAll("p"); // Retourne un tableau d'objets correspondant à tous les "p"
document.querySelectorAll(".item"); // Retourne un tableau d'objets correspondant à tous les éléments qui ont la class "item"
Sauvegarder sa sélection
Il s’avère bien souvent utile de sauvegarder une sélection dans une variable pour éviter de rappeler plusieurs fois une fonction de sélection (comme querySelector()
par exemple) dans le cas où l’on souhaite appliquer plusieurs traitements sur un élément.
En réutilisant cette variable, on gagnera ainsi en performance.
const welcomeMessage = document.querySelector("#welcome-message");
// Je peux désormais effectuer plusieurs traitements sur welcomeMessage sans avoir à répéter ma sélection
Une sélection visant à stocker la référence à un ou plusieurs nœud(s) du DOM, nous avons rarement besoin de redéfinir cette variable. Il est donc préférable de déclarer cette variable en tant que constante (avec le mot-clé const
), afin d’éviter toute réaffectation accidentelle. Dans le cas où vous auriez besoin de la réaffecter, il sera alors préférable d’utiliser let
.
Préciser sa sélection
Cette sélection peut se faire sur l’ensemble du document HTML avec le préfixe document
, mais aussi sur une autre sélection en la préfixant de la variable la contenant.
<section id="section-music">
<article class="product"></article>
<article class="product"></article>
<article class="product"></article>
</section>
<section id="section-sport">
<article class="product"></article>
<article class="product"></article>
<article class="product"></article>
</section>
// On cible l'élément qui a l'id "section-sport" sur l'ensemble du document
const sectionSport = document.getElementById("section-sport");
// On ne cible les éléments qui ont la class "product" au sein du conteneur qui a l'id "section-sport" et non pas dans tout le document HTML
const articles = sectionSport.getElementsByClassName("product");
Manipuler le DOM
Une fois que nous avons ciblé un élément avec un sélecteur, il est possible de le manipuler en récupérant ses caractéristiques (lecture) mais aussi en les modifiant (écriture) via des propriétés et des méthodes.
Manipuler les nœuds existants
Contenu des nœuds
Textuels ou HTML, il est possible d’accéder aux nœuds d’un document en lecture ou écriture.
Nœud textuel
La propriété element.textContent
permet de récupérer ou de redéfinir le texte (uniquement - pas les balises HTML) contenu dans l’élément sélectionné.
<p>Hello les <span class="highlight">devs</span> !</p>
const p = document.querySelector("p");
// Récupère le texte "Hello les devs !"
const pText = p.textContent;
// Redéfinit le contenu textuel de la balise "p" par "Nouveau texte"
p.textContent = "Nouveau texte";
Nœud HTML
La propriété element.innerHTML
permet de récupérer ou de redéfinir le HTML contenu dans l’élément sélectionné.
<p>Hello les <span class="highlight">devs</span> !</p>
const p = document.querySelector("p");
// Récupère le HTML "Hello les <span class='highlight'>devs</span> !"
const pHTML = p.innerHTML;
// Redéfinit le contenu HTML de la balise "p" par "Nouveau HTML"
p.innerHTML = "Nouveau HTML";
Style CSS d’un nœud
<p style="color: red">laConsole - Apprendre le développement web</p>
La propriété element.style.property
permet de récupérer ou de modifier la valeur d’une propriété CSS sur l’élément sélectionné. property
étant le nom de la propriété à récupérer / modifier. Cette propriété est forcément définie via une règle de CSS inline
(= directement sur la balise via l’attribut HTML style
).
const p = document.querySelector("p");
// Récupère la valeur de la propriété "color" du premier paragraphe (ici "red")
let pColor = p.style.color;
// Redéfinit la valeur de la propriété "color" du premier paragraphe par "green"
p.style.color = "green";
Si on veut récupérer du CSS défini dans une feuille de style à part, on fait appel aux méthodes element.getComputedStyle()
et getPropertyValue()
. Il ne sera nativement pas possible d’y écrire dedans avec JavaScript qui est un langage côté client et non serveur.
window.getComputedStyle(document.querySelector("p")).getPropertyValue('font-size');
Attributs génériques
La méthode element.getAttribute(attribute)
permet de récupérer la valeur d’un attribut spécifié.
<img id="logo" src="https://laconsole.dev/demo.png" alt="Image de démo">
const logo = document.querySelector("#logo");
// Retourne la valeur de l'attribut "src", ici "https://laconsole.dev/demo.png"
logo.getAttribute("src");
La méthode element.setAttribute(attribute, value)
permet de définir la valeur d’un attribut spécifié.
<img id="logo" src="https://laconsole.dev/demo.png" alt="Image de démo">
const logo = document.querySelector("#logo");
// Modifie la valeur de l'attribut "src", ici "https://laconsole.dev/demo2.png"
logo.setAttribute("src", "https://laconsole.dev/demo2.png");
Classes
La manipulation de l’attribut class
est très fréquente. En ce sens, on ne les manipulera pas via getAttribute()
et setAttribute()
mais via une propriété classList
dédiée. Pourquoi cela ? Car nous avons souvent besoin de toggler une classe, d’ajouter plusieurs classes à un élément, etc.
En appelant la propriété classList
sur un élément, on pourra lister les classes associées.
<a href="#" class="btn btn-primary" id="download">Télécharger</a>
// Retourne la liste ["btn", "btn-primary"]
document.querySelector("#download").classList;
Cette propriété fournit également une série de méthodes permettant de manipuler cette liste de classes.
element.classList.add(class, class...)
: ajoute une ou plusieurs classe(s) à un élément.
// Ajoute les classes "btn-outline" et "active"
document.querySelector("#download").classList.add("btn-outlined", "active");
element.classList.remove(class, class...)
: supprime une ou plusieurs classe(s) à un élément.
// Supprime la classe "active"
document.querySelector("#download").classList.remove("active");
element.classList.toggle(class)
: ajoute (si elle n’existe pas) ou supprime (si elle existe) la classe spécifiée en paramètre.
// Ajoute la classe "active" si absente ou la supprime si présente
document.querySelector("#download").classList.toggle("active");
element.classList.contains(class)
: vérifie si la classe spécifiée est contenue par l’élément.
- Si présente, retourne
true
- Si absente, retourne
false
// Vérifie si la classe "active" est présente sur l'élément
document.querySelector("#download").classList.contains("active");
element.classList.replace(oldClass, newClass)
: remplace une classe par une autre.
// Remplace la classe "btn-primary" par "btn-secondary"
document.querySelector("#download").classList.replace("btn-primary", "btn-secondary");
Sur la propriété classList
en lecture seule, il est aussi possible de faire appel à d’autres propriétés et méthodes, plus secondaires, non abordées dans cette formation.
Nœuds de formulaire
Très souvent manipulés via JavaScript, tout comme les classes, les formulaires ont droit à un traitement à part.
La propriété element.value
permet de récupérer ou de redéfinir la valeur d’un champ de formulaire.
<input type="text" placeholder="Nom d'utilisateur" id="username">
const usernameField = document.querySelector("#username");
const usernameFieldValue = usernameField.value; // Récupère la valeur saisie dans le champ
usernameField.value = "Toto"; // Redéfinit la valeur du champ
Créer un nœud
Si un élément n’existe pas encore dans l’arborescence de votre page, vous avez la possibilité de le créer avec la méthode createElement(element)
, puis d’y ajouter du texte dedans avec la méthode createTextNode(text)
.
const p = document.createElement("p");
const text = document.createTextNode("Hello !");
Insérer un nœud
Pour insérer un élément dans le DOM, on fait appel aux méthodes suivantes :
prepend()
: insertion en tant que premier enfant d’un élément.append()
: insertion en tant que dernier enfant d’un élément.before()
: insertion avant un élément.after()
: insertion après un élément.
Considérons la structure HTML suivante :
<main>
<p>Paragraphe 1</p>
<p>Paragraphe 2</p>
<p>Paragraphe 3</p>
</main>
// Je cible la balise "main"
const main = document.querySelector('main');
// Création d'une balise "p"
const p1 = document.createElement('p');
// Création d'un texte "Avant !"
const textP1 = document.createTextNode('Avant !');
// Insertion du texte en dernier enfant de la balise "p" ("prepend" aurait eu le même effet la balise étant vide)
p1.append(textP1);
// Insertion de la balise "p" avant la balise "main"
main.before(p1);
// C'est la même logique pour ajouter un paragraphe en premier / dernier enfant, ou après la balise main...
<p>Avant !</p>
<main>
<p>Premier enfant !</p>
<p>Paragraphe 1</p>
<p>Paragraphe 2</p>
<p>Paragraphe 3</p>
<p>Dernier enfant !</p>
</main>
<p>Après !</p>
Supprimer un nœud
Pour supprimer un élément du DOM, on fait appel à la méthode removeChild(child)
.
Considérons la structure HTML suivante :
<main>
<p>Paragraphe 1</p>
<p id="to-delete">Paragraphe 2</p>
<p>Paragraphe 3</p>
</main>
// Je cible la balise "main"
const main = document.querySelector('main');
// Je cible le second "p"
const pToDelete = document.querySelector('#to-delete');
// Suppression du second paragraphe
main.removeChild(pToDelete);
Autres opérations
Il est également possible d’utiliser les méthodes cloneNode()
ou encore replaceChild()
pour respectivement dupliquer et remplacer des nœuds.