034.4 Leçon 1
Certification : |
Web Development Essentials |
---|---|
Version : |
1.0 |
Thème : |
034 Programmation JavaScript |
Objectif : |
034.4 Manipulation en JavaScript du contenu et du style des sites web |
Leçon : |
1 sur 1 |
Introduction
HTML, CSS et JavaScript sont trois technologies distinctes qui se rejoignent sur le web. Pour créer des pages véritablement dynamiques et interactives, le programmeur JavaScript doit combiner des composants HTML et CSS au moment de l’exécution, une tâche qui est grandement facilitée par l’utilisation du modèle objet de document (DOM : Document Object Model).
Interagir avec le DOM
Le DOM est une structure de données qui fonctionne comme une interface de programmation pour le document, où chaque aspect du document est représenté comme un nœud dans le DOM et chaque changement apporté au DOM se répercute immédiatement dans le document. Pour montrer comment utiliser le DOM en JavaScript, enregistrez le code HTML suivant dans un fichier appelé example.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first">
<p>The dynamic content goes here</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section</p>
</div><!-- #content_second -->
</body>
</html>
Le DOM ne sera disponible qu’après le chargement du HTML, donc écrivez le code JavaScript suivant à la fin du corps de la page (avant la balise de fin </body>
) :
<script>
let body = document.getElementsByTagName("body")[0];
console.log(body.innerHTML);
</script>
L’objet document
est l’élément supérieur du DOM, tous les autres éléments en sont dérivés. La méthode getElementsByTagName()
liste tous les éléments descendant de document
qui ont le nom de balise donné. Même si la balise body
n’est utilisée qu’une seule fois dans le document, la méthode getElementsByTagName()
renvoie toujours un tableau des éléments trouvés, d’où l’utilisation de l’index [0]
pour renvoyer le premier (et seul) élément trouvé.
Contenu HTML
Comme le montre l’exemple précédent, l’élément DOM renvoyé par la commande document.getElementsByTagName("body")[0]
a été affecté à la variable body
. La variable body
peut alors être utilisée pour manipuler l’élément '<body>' de la page, car elle hérite de toutes les méthodes et attributs DOM de cet élément. Par exemple, la propriété innerHTML
contient l’intégralité du code de balisage HTML écrit à l’intérieur de l’élément correspondant, elle peut donc être utilisée pour lire le balisage interne. Notre appel console.log(body.innerHTML)
affiche le contenu de <body></body>
dans la console web. La variable peut également être utilisée pour remplacer ce contenu, comme dans body.innerHTML = "<p>Content erased</p>"
.
Plutôt que de modifier des portions entières du balisage HTML, il est plus pratique de ne pas modifier la structure du document et de n’interagir qu’avec ses éléments. Une fois le document rendu par le navigateur, tous les éléments sont accessibles par les méthodes DOM. Il est possible, par exemple, de lister et d’accéder à tous les éléments HTML en utilisant la chaîne spéciale *
dans la méthode getElementsByTagName()
de l’objet document
:
let elements = document.getElementsByTagName("*");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Ce code va placer tous les éléments trouvés dans document
dans la variable elements
. La variable elements
est un objet de type tableau, nous pouvons donc itérer à travers chacun de ses éléments avec une boucle for
. Si la page HTML sur laquelle s’exécute ce code contient un élément dont l’attribut id
est défini sur content_first
(voir l’exemple de page HTML présenté au début de la leçon), l’instruction if
correspond à cet élément et son contenu sera modifié en <p>New content</p>
. Notez que les attributs d’un élément HTML dans le DOM sont accessibles en utilisant la notation point des propriétés des objets JavaScript : ainsi, element.id
fait référence à l’attribut id
de l’élément courant de la boucle for
. La méthode getAttribute()
peut également être utilisée, comme dans element.getAttribute("id")
.
Il n’est pas nécessaire d’itérer à travers tous les éléments si vous voulez inspecter seulement un sous-ensemble d’entre eux. Par exemple, la méthode document.getElementsByClassName()
limite les éléments trouvés à ceux qui ont une classe spécifique :
let elements = document.getElementsByClassName("content");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Cependant, l’itération à travers de nombreux éléments du document à l’aide d’une boucle n’est pas la meilleure stratégie lorsque vous devez modifier un élément spécifique de la page.
Sélection d’éléments spécifiques
JavaScript fournit des méthodes optimisées pour sélectionner l’élément exact sur lequel vous voulez travailler. La boucle précédente pourrait être entièrement remplacée par la méthode document.getElementById()
:
let element = document.getElementById("content_first");
element.innerHTML = "<p>New content</p>";
Chaque attribut id
du document doit être unique, donc la méthode document.getElementById()
ne retourne qu’un seul objet DOM. Même la déclaration de la variable element
peut être omise, car JavaScript nous permet d’enchaîner directement les méthodes :
document.getElementById("content_first").innerHTML = "<p>New content</p>";
La méthode getElementById()
est la méthode préférable pour localiser des éléments dans le DOM, car ses performances sont bien meilleures que les méthodes itératives lorsqu’on travaille avec des documents complexes. Cependant, tous les éléments n’ont pas un identifiant explicite, et la méthode renvoie une valeur null si aucun élément ne correspond à l’identifiant fourni (cela empêche également l’utilisation d’attributs ou de fonctions enchaînés, comme le innerHTML
utilisé dans l’exemple ci-dessus). De plus, il est plus pratique d’attribuer des attributs id
uniquement aux principaux composants de la page, puis d’utiliser des sélecteurs CSS pour localiser leurs éléments enfants.
Les sélecteurs, présentés dans une leçon précédente sur les CSS, sont des motifs qui correspondent à des éléments dans le DOM. La méthode querySelector()
renvoie le premier élément correspondant dans l’arbre du DOM, tandis que querySelectorAll()
renvoie tous les éléments qui correspondent au sélecteur spécifié.
Dans l’exemple précédent, la méthode getElementById()
récupère l’élément portant l’identifiant content_first
. La méthode querySelector()
peut effectuer la même tâche :
document.querySelector("#content_first").innerHTML = "<p>New content</p>";
Comme la méthode querySelector()
utilise la syntaxe des sélecteurs, l’identifiant fourni doit commencer par un caractère dièse. Si aucun élément correspondant n’est trouvé, la méthode querySelector()
renvoie null.
Dans l’exemple précédent, le contenu entier de la section content_first
est remplacé par la chaîne de texte fournie. Cette chaîne contient du code HTML, ce qui n’est pas considéré comme une bonne pratique. Vous devez être prudent lorsque vous ajoutez des balises HTML codées en dur au code JavaScript, car le suivi des éléments peut devenir difficile lorsque des modifications de la structure globale du document sont nécessaires.
Les sélecteurs ne sont pas limités à l’identifiant de l’élément. L’élément interne p
peut être adressé directement :
document.querySelector("#content_first p").innerHTML = "New content";
Le sélecteur #content_first p
ne correspondra qu’au premier élément p
à l’intérieur de la section #content_first
. Cela fonctionne bien si nous voulons manipuler le premier élément. Cependant, nous pouvons vouloir modifier le deuxième paragraphe :
<div class="content" id="content_first">
<p>Don't change this paragraph.</p>
<p>The dynamic content goes here.</p>
</div><!-- #content_first -->
Dans ce cas, nous pouvons utiliser la pseudo-classe :nth-child(2)
pour faire correspondre le deuxième élément p
:
document.querySelector("#content_first p:nth-child(2)").innerHTML = "New content";
Le chiffre 2
dans p:nth-child(2)
indique le deuxième paragraphe qui correspond au sélecteur. Consultez la leçon sur les sélecteurs CSS pour en savoir plus sur les sélecteurs et leur utilisation.
Travailler avec les attributs
La capacité de JavaScript à interagir avec le DOM ne se limite pas à la manipulation du contenu. En effet, l’utilisation la plus répandue de JavaScript dans le navigateur consiste à modifier les attributs des éléments HTML existants.
Disons que notre page d’exemple HTML originale comporte maintenant trois sections de contenu :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first" hidden>
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third" hidden>
<p>Third section.</p>
</div><!-- #content_third -->
</body>
</html>
Vous pouvez vouloir n’en rendre qu’une seule visible à la fois, d’où l’attribut hidden
dans toutes les balises div
. C’est utile, par exemple, pour ne montrer qu’une seule image d’une galerie d’images. Pour rendre l’une d’entre elles visible au chargement de la page, ajoutez le code JavaScript suivant à la page :
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).removeAttribute("hidden");
L’expression évaluée par l’instruction switch
renvoie aléatoirement le nombre 0, 1 ou 2. L’identifiant du sélecteur correspondant est alors assigné à la variable content_visible
, qui est utilisée par la méthode querySelector(content_visible)
. L’appel enchaîné removeAttribute("hidden")
supprime l’attribut hidden
de l’élément.
L’approche inverse est également possible : Toutes les sections pourraient être initialement visibles (sans l’attribut hidden
) et le programme JavaScript peut alors attribuer l’attribut hidden
à toutes les sections sauf celle qui se trouve dans content_visible
. Pour ce faire, vous devez itérer à travers le contenu de tous les éléments div
qui sont différents de celui choisi, ce qui peut être fait en utilisant la méthode querySelectorAll()
:
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
// Hide all content divs, except content_visible
for ( element of document.querySelectorAll(".content:not("+content_visible+")") )
{
// Hidden is a boolean attribute, so any value will enable it
element.setAttribute("hidden", "");
}
Si la variable content_visible
a été définie sur #content_first
, le sélecteur sera .content:not(#content_first)
, ce qui signifie que tous les éléments ayant la classe content
sauf ceux ayant l’identifiant content_first
. La méthode setAttribute()
ajoute ou modifie les attributs des éléments HTML. Son premier paramètre est le nom de l’attribut et le second est la valeur de l’attribut.
Cependant, le moyen le plus approprié pour modifier l’apparence des éléments est le CSS. Dans ce cas, nous pouvons définir la propriété CSS display
sur hidden
et ensuite la changer en block
en utilisant JavaScript :
<style>
div.content { display: none }
</style>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
<script>
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).style.display = "block";
</script>
Les mêmes bonnes pratiques qui s’appliquent au mélange de balises HTML et de JavaScript s’appliquent également au CSS. Ainsi, il n’est pas recommandé d’écrire les propriétés CSS directement dans le code JavaScript. Au contraire, les règles CSS doivent être écrites séparément du code JavaScript. La bonne façon d’alterner le style visuel est de sélectionner une classe CSS prédéfinie pour l’élément.
Travailler avec les classes
Les éléments peuvent avoir plus d’une classe associée, ce qui facilite l’écriture de styles qui peuvent être ajoutés ou supprimés si nécessaire. Il serait épuisant de modifier de nombreux attributs CSS directement en JavaScript. Vous pouvez donc créer une nouvelle classe CSS avec ces attributs, puis ajouter la classe à l’élément. Les éléments DOM possèdent la propriété classList
, qui peut être utilisée pour visualiser et manipuler les classes attribuées à l’élément correspondant.
Par exemple, au lieu de modifier la visibilité de l’élément, nous pouvons créer une classe CSS supplémentaire pour mettre en évidence le contenu content
de notre élément div
:
div.content {
border: 1px solid black;
opacity: 0.25;
}
div.content.highlight {
border: 1px solid red;
opacity: 1;
}
Cette feuille de style va ajouter une fine bordure noire et une semi-transparence à tous les éléments ayant la classe content
. Seuls les éléments qui ont également la classe highlight
seront totalement opaques et auront une fine bordure rouge. Ensuite, au lieu de modifier les propriétés CSS directement comme nous l’avons fait auparavant, nous pouvons utiliser la méthode classList.add("highlight")
dans l’élément sélectionné :
// Which content to highlight
let content_highlight;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_highlight = "#content_first";
break;
case 1:
content_highlight = "#content_second";
break;
case 2:
content_highlight = "#content_third";
break;
}
// Highlight the selected div
document.querySelector(content_highlight).classList.add("highlight");
Toutes les techniques et tous les exemples que nous avons vus jusqu’à présent ont été réalisés à la fin du processus de chargement de la page, mais ils ne sont pas limités à cette étape. En fait, ce qui rend JavaScript si utile aux développeurs web, c’est sa capacité à réagir aux événements survenant sur la page, ce que nous verrons ensuite.
Gestionnaires d’événements
Tous les éléments visibles de la page sont susceptibles de subir des événements interactifs, tels que le clic ou le mouvement de la souris elle-même. Nous pouvons associer des actions personnalisées à ces événements, ce qui étend considérablement les possibilités d’un document HTML.
L’élément HTML le plus évident qui bénéficie d’une action associée est probablement l’élément bouton
. Pour montrer comment cela fonctionne, ajoutez trois boutons au-dessus du premier élément div
de la page d’exemple :
<p>
<button>First</button>
<button>Second</button>
<button>Third</button>
</p>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
Les boutons ne font rien par eux-mêmes, mais supposons que vous vouliez mettre en évidence la div
correspondant au bouton pressé. Nous pouvons utiliser l’attribut onClick
pour associer une action à chaque bouton :
<p>
<button onClick="document.getElementById('content_first').classList.toggle('highlight')">First</button>
<button onClick="document.getElementById('content_second').classList.toggle('highlight')">Second</button>
<button onClick="document.getElementById('content_third').classList.toggle('highlight')">Third</button>
</p>
La méthode classList.toggle()
ajoute la classe spécifiée à l’élément si elle n’est pas présente, et supprime cette classe si elle est déjà présente. Si vous exécutez l’exemple, vous remarquerez que plus d`un div
peut être mis en évidence en même temps. Pour mettre en évidence uniquement le div
correspondant au bouton pressé, il est nécessaire de supprimer la classe highlight
des autres éléments div
. Néanmoins, si l’action personnalisée est trop longue ou implique plus d’une ligne de code, il est plus pratique d’écrire une fonction en dehors de la balise de l’élément :
function highlight(id)
{
// Remove the "highlight" class from all content elements
for ( element of document.querySelectorAll(".content") )
{
element.classList.remove('highlight');
}
// Add the "highlight" class to the corresponding element
document.getElementById(id).classList.add('highlight');
}
Comme les exemples précédents, cette fonction peut être placée dans une balise <script>
ou dans un fichier JavaScript externe associé au document. La fonction highlight
supprime d’abord la classe highlight
de tous les éléments div
associés à la classe content
, puis ajoute la classe highlight
à l’élément choisi. Chaque bouton doit ensuite appeler cette fonction depuis son attribut onClick
, en utilisant l’identifiant correspondant comme argument de la fonction :
<p>
<button onClick="highlight('content_first')">First</button>
<button onClick="highlight('content_second')">Second</button>
<button onClick="highlight('content_third')">Third</button>
</p>
En plus de l’attribut onClick
, nous pourrions utiliser l’attribut onMouseOver
(déclenché lorsque le dispositif de pointage est utilisé pour déplacer le curseur sur l’élément), l’attribut onMouseOut
(déclenché lorsque le dispositif de pointage n’est plus contenu dans l’élément), etc. De plus, les gestionnaires d’événements ne sont pas limités aux boutons, vous pouvez donc leur attribuer des actions personnalisées pour tous les éléments HTML visibles.
Exercices guidés
-
En utilisant la méthode
document.getElementById()
, comment pourriez-vous insérer la phrase “Dynamic content” dans le contenu interne de l’élément dont l’identifiant estmessage
? -
Quelle est la différence entre référencer un élément par son identifiant en utilisant la méthode
document.querySelector()
et le faire via la méthodedocument.getElementById()
? -
Quel est l’objectif de la méthode
classList.remove()
? -
Quel est le résultat de l’utilisation de la méthode
myelement.classList.toggle("active")
simyelement
n’est pas associé à la classeactive
?
Exercices d’exploration
-
Quel argument de la méthode
document.querySelectorAll()
lui fera imiter la méthodedocument.getElementsByTagName("input")
? -
Comment utiliser la propriété
classList
pour lister toutes les classes associées à un élément donné ?
Résumé
Cette leçon explique comment utiliser JavaScript pour modifier les contenus HTML et leurs propriétés CSS à l’aide du DOM (Document Object Model). Ces modifications peuvent être déclenchées par des événements utilisateur, ce qui est utile pour créer des interfaces dynamiques. La leçon aborde les concepts et procédures suivants :
-
Comment inspecter la structure du document en utilisant des méthodes comme
document.getElementById()
,document.getElementsByClassName()
,document.getElementsByTagName()
,document.querySelector()
etdocument.querySelectorAll()
. -
Comment modifier le contenu du document avec la propriété
innerHTML
. -
Comment ajouter et modifier les attributs des éléments de la page avec les méthodes
setAttribute()
etremoveAttribute()
. -
La manière correcte pour manipuler les classes d’éléments en utilisant la propriété
classList
et sa relation avec les styles CSS. -
Comment lier des fonctions aux événements de souris dans des éléments spécifiques.
Réponses aux exercices guidés
-
En utilisant la méthode
document.getElementById()
, comment pourriez-vous insérer la phrase “Dynamic content” dans le contenu interne de l’élément dont l’identifiant estmessage
?Vous pouvez le faire avec la propriété
innerHTML
:document.getElementById("message").innerHTML = "Dynamic content"
-
Quelle est la différence entre référencer un élément par son identifiant en utilisant la méthode
document.querySelector()
et le faire via la méthodedocument.getElementById()
?L’identifiant doit être accompagné du caractère dièse dans les fonctions qui utilisent des sélecteurs, comme
document.querySelector()
. -
Quel est l’objectif de la méthode
classList.remove()
?Elle supprime la classe (dont le nom est donné en argument de la fonction) de l’attribut
class
de l’élément correspondant. -
Quel est le résultat de l’utilisation de la méthode
myelement.classList.toggle("active")
simyelement
n’est pas associé à la classeactive
?Cette méthode attribuera la classe
active
àmyelement
.
Réponses aux exercices d’exploration
-
Quel argument de la méthode
document.querySelectorAll()
lui fera imiter la méthodedocument.getElementsByTagName("input")
?En utilisant la méthode
document.querySelectorAll("input")
, vous ferez correspondre tous les élémentsinput
de la page, tout commedocument.getElementsByTagName("input")
. -
Comment utiliser la propriété
classList
pour lister toutes les classes associées à un élément donné ?La propriété
classList
est un objet de type tableau, donc une bouclefor
peut être utilisée pour itérer à travers toutes les classes qu’elle contient.