Manipuler le code HTML (partie 2/2)

La propriétéinnerHTMLa comme principale qualité d'être facile et rapide à utiliser et c'est la raison pour laquelle elle est généralement privilégiée par les débutants, et même par de nombreux développeurs expérimentés.innerHTMLa longtemps été une propriété non standardisée, mais depuis le HTML5 elle est reconnue par le W3C et peut donc être utilisée sans trop se poser de questions.

Dans ce deuxième chapitre consacré à la manipulation du contenu, nous allons aborder la modification du document via le DOM. Nous l'avons déjà fait dans le premier chapitre, notamment avecsetAttribute(). Mais ici il va s'agir de créer, supprimer et déplacer des éléments HTML. C'est un gros morceau du JavaScript, pas toujours facile à assimiler. Vous allez me dire, siinnerHTMLsuffit, pourquoi devoir s'embêter avec le DOM ? Tout simplement parce que le DOM est plus puissant et est nécessaire pour traiter du XML.

Naviguer entre les nœuds

Nous avons vu précédemment qu'on utilisait les méthodesgetElementById()etgetElementsByTagName()pour accéder aux éléments HTML. Une fois que l'on a atteint un élément, il est possible de se déplacer de façon un peu plus précise, avec toute une série de méthodes et de propriétés que nous allons étudier ici.

La propriétéparentNode

La propriétéparentNodepermet d'accéder à l'élément parent d'un élément. Regardez ce code :

<blockquote>
<p id="myP">Ceci est un paragraphe !</p>
</blockquote>

Admettons qu'on doive accéder àmyP, et que pour une autre raison on doive accéder à l'élément<blockquote>, qui est le parent demyP. Il suffit d'accéder àmyPpuis à son parent, avecparentNode:

var paragraph = document.getElementById('myP');
var blockquote = paragraph.parentNode;

nodeTypeetnodeName

nodeTypeetnodeNameservent respectivement à vérifier le type d'un nœud et le nom d'un nœud.nodeTyperetourne un nombre, qui correspond à un type de nœud. Voici un tableau qui liste les types possibles, ainsi que leurs numéros (les types courants sont mis en gras) :

Numéro

Type de nœud

1

Nœud élément

2

Nœud attribut

3

Nœud texte

4

Nœud pour passage CDATA (relatif au XML)

5

Nœud pour référence d'entité

6

Nœud pour entité

7

Nœud pour instruction de traitement

8

Nœud pour commentaire

9

Nœud document

10

Nœud type de document

11

Nœud de fragment de document

12

Nœud pour notation

nodeName, quant à lui, retourne simplement le nom de l'élément, en majuscule. Il est toutefois conseillé d'utilisertoLowerCase()(outoUpperCase()) pour forcer un format de casse et ainsi éviter les mauvaises surprises.

var paragraph = document.getElementById('myP');
alert(paragraph.nodeType + '\n\n' + paragraph.nodeName.toLowerCase());

Essayer le code

Lister et parcourir des nœuds enfants

firstChildetlastChild

Comme leur nom le laisse présager,firstChildetlastChildservent respectivement à accéder au premier et au dernier enfant d'un nœud.

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Le titre de la page</title>
</head>
<body>
<div>
<p id="myP">Un peu de texte, <a>un lien</a> et <strong>une portion en emphase</strong></p>
</div>
<script>
var paragraph = document.getElementById('myP');
var first = paragraph.firstChild;
var last = paragraph.lastChild;
alert(first.nodeName.toLowerCase());
alert(last.nodeName.toLowerCase());
</script>
</body>
</html>

Essayer le code

En schématisant l'élémentmyPprécédent, on obtient ceci :

Schéma de l'élément myP
Schéma de l'élément myP

Le premier enfant de<p>est un nœud textuel, alors que le dernier enfant est un élément<strong>.

nodeValueetdata

Changeons de problème : il faut récupérer le texte du premier enfant, et le texte contenu dans l'élément<strong>, mais comment faire ?

Il faut soit utiliser la propriéténodeValuesoit la propriétédata. Si on recode le script précédent, nous obtenons ceci :

var paragraph = document.getElementById('myP');
var first = paragraph.firstChild;
var last = paragraph.lastChild;
alert(first.nodeValue);
alert(last.firstChild.data);

Essayer le code

firstcontient le premier nœud, un nœud textuel. Il suffit de lui appliquer la propriéténodeValue(oudata) pour récupérer son contenu ; pas de difficulté ici. En revanche, il y a une petite différence avec notre élément<strong>: vu que les propriétésnodeValueetdatane s'appliquent que sur des nœuds textuels, il nous faut d'abord accéder au nœud textuel que contient notre élément, c'est-à-dire son nœud enfant. Pour cela, on utilisefirstChild(et non pasfirstElementChild), et ensuite on récupère le contenu avecnodeValueoudata.

childNodes

La propriétéchildNodesretourne un tableau contenant la liste des enfants d'un élément. L'exemple suivant illustre le fonctionnement de cette propriété, de manière à récupérer le contenu des éléments enfants :

<body>
<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
<script>
var paragraph = document.getElementById('myP');
var children = paragraph.childNodes;
for (var i = 0, c = children.length; i < c; i++) {
if (children[i].nodeType === Node.ELEMENT_NODE) { // C'est un élément HTML
alert(children[i].firstChild.data);
} else { // C'est certainement un nœud textuel
alert(children[i].data);
}
}
</script>
</body>

Essayer le code

nextSiblingetpreviousSibling

nextSiblingetpreviousSiblingsont deux propriétés qui permettent d'accéder respectivement au nœud suivant et au nœud précédent.

<body>
<div>
<p id="myP">Un peu de texte, <a>un lien</a> et <strong>une portion en emphase</strong></p>
</div>
<script>
var paragraph = document.getElementById('myP');
var first = paragraph.firstChild;
var next = first.nextSibling;
alert(next.firstChild.data); // Affiche « un lien »
</script>
</body>

Essayer le code

Dans cet exemple, on récupère le premier enfant demyP, et sur ce premier enfant on utilisenextSibling, qui permet de récupérer l’élément<a>. Avec ça, il est même possible de parcourir les enfants d'un élément, en utilisant une bouclewhile:

<body>
<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
<script>
var paragraph = document.getElementById('myP');
var child = paragraph.lastChild; // On prend le dernier enfant
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) { // C'est un élément HTML
alert(child.firstChild.data);
} else { // C'est certainement un nœud textuel
alert(child.data);
}
child = child.previousSibling; // À chaque tour de boucle, on prend l'enfant précédent
}
</script>
</body>

Essayer le code

Pour changer un peu, la boucle tourne « à l'envers », car on commence par récupérer le dernier enfant et on chemine à reculons.

Attention aux nœuds vides

En considérant le code HTML suivant, on peut penser que l'élément<div>ne contient que trois enfants<p>:

<div>
<p>Paragraphe 1</p>
<p>Paragraphe 2</p>
<p>Paragraphe 3</p>
</div>

Mais attention, car ce code est radicalement différent de celui-ci :

<div><p>Paragraphe 1</p><p>Paragraphe 2</p><p>Paragraphe 3</p></div>

En fait, les espaces entre les éléments tout comme les retours à la ligne sont considérés comme des nœuds textuels (enfin, cela dépend des navigateurs) ! Ainsi donc, si l'on schématise le premier code, on obtient ceci :

Schéma de notre premier code
Schéma de notre premier code

Alors que le deuxième code peut être schématisé comme ça :

Schéma de notre deuxième code
Schéma de notre deuxième code

Heureusement, il existe une solution à ce problème ! Les attributsfirstElementChild,lastElementChild,nextElementSiblingetpreviousElementSiblingne retournent que des éléments HTML et permettent donc d'ignorer les nœuds textuels. Ils s'utilisent exactement de la même manière que les attributs de base (firstChild,lastChild, etc.). Attention, ces attributs ne sont pas supportés par les versions d'Internet Explorer antérieures à la 9.

Créer et insérer des éléments

Ajouter des éléments HTML

Avec le DOM, l'ajout d'un élément HTML se fait en trois temps :

  1. On crée l'élément ;

  2. On lui affecte des attributs ;

  3. On l'insère dans le document, et ce n'est qu'à ce moment-là qu'il sera « ajouté ».

Création de l'élément

La création d'un élément se fait avec la méthodecreateElement(), un sous-objet de l'objet racine, c'est-à-diredocumentdans la majorité des cas :

var newLink = document.createElement('a');

On crée ici un nouvel élément<a>. Cet élément est créé, mais n'est pas inséré dans le document, il n'est donc pas visible. Cela dit, on peut déjà travailler dessus, en lui ajoutant des attributs ou même des événements (que nous verrons dans le chapitre suivant).

Affectation des attributs

Ici, c'est comme nous avons vu précédemment : on définit les attributs, soit avecsetAttribute(), soit directement avec les propriétés adéquates.

newLink.id = 'sdz_link';
newLink.href = 'http://www.siteduzero.com';
newLink.title = 'Découvrez le Site du Zéro !';
newLink.setAttribute('tabindex', '10');
Insertion de l'élément

On utilise la méthodeappendChild()pour insérer l'élément. Append child signifie « ajouter un enfant », ce qui signifie qu'il nous faut connaître l'élément auquel on va ajouter l'élément créé. Considérons donc le code suivant :

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Le titre de la page</title>
</head>
<body>
<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
</body>
</html>

On va ajouter notre élément<a>dans l'élément<p>portant l'IDmyP. Pour ce faire, il suffit de récupérer cet élément, et d'ajouter notre élément<a>viaappendChild():

document.getElementById('myP').appendChild(newLink);

Une petite explication s'impose ! Avant d'insérer notre élément<a>, la structure DOM du document ressemble à ceci :

Avant d'insérer notre élément <a>, la structure DOM du document ressemble à cette figure
Avant d'insérer notre élément <a>, la structure DOM du document ressemble à cette figure

On voit que l'élément<a>existe, mais n'est pas lié. Un peu comme s'il était libre dans le document : il n'est pas encore placé. Le but est de le placer comme enfant de l'élémentmyP. La méthodeappendChild()va alors déplacer notre<a>pour le placer en tant que dernier enfant demyP:

L'élément est placé en tant que dernier enfant
L'élément est placé en tant que dernier enfant

Cela veut dire qu'appendChild()insérera toujours l'élément en tant que dernier enfant, ce qui n'est pas toujours très pratique. Nous verrons plus tard comment insérer un élément avant ou après un enfant donné.

Ajouter des nœuds textuels

L'élément a été inséré, seulement il manque quelque chose : le contenu textuel ! La méthodecreateTextNode()sert à créer un nœud textuel (de type#text) qu'il nous suffira d'ajouter à notre élément fraîchement inséré, comme ceci :

var newLinkText = document.createTextNode("Le Site du Zéro");
newLink.appendChild(newLinkText);

L'insertion se fait ici aussi avecappendChild(), sur l'élémentnewLink. Afin d'y voir plus clair, résumons le code :

<body>
<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
<script>
var newLink = document.createElement('a');
newLink.id = 'sdz_link';
newLink.href = 'http://www.siteduzero.com';
newLink.title = 'Découvrez le Site du Zéro !';
newLink.setAttribute('tabindex', '10');
document.getElementById('myP').appendChild(newLink);
var newLinkText = document.createTextNode("Le Site du Zéro");
newLink.appendChild(newLinkText);
</script>
</body>

Essayer le code

Voici donc ce qu'on obtient, sous forme schématisée :

Le résultat obtenu
Le résultat obtenu

Il y a quelque chose à savoir : le fait d'insérer viaappendChild()n'a aucune incidence sur l'ordre d’exécution des instructions. Cela veut donc dire que l'on peut travailler sur les éléments HTML et les nœuds textuels sans qu'ils soient au préalable insérés dans le document. Par exemple, on pourrait ordonner le code comme ceci :

var newLink = document.createElement('a');
var newLinkText = document.createTextNode("Le Site du Zéro");
newLink.id = 'sdz_link';
newLink.href = 'http://www.siteduzero.com';
newLink.title = 'Découvrez le Site du Zéro !';
newLink.setAttribute('tabindex', '10');
newLink.appendChild(newLinkText);
document.getElementById('myP').appendChild(newLink);

Ici, on commence par créer les deux éléments (le lien et le nœud de texte), puis on affecte les variables au lien et on lui ajoute le nœud textuel. À ce stade-ci, l'élément HTML contient le nœud textuel, mais cet élément n'est pas encore inséré dans le document :

L'élément HTML contient le nœud textuel, mais cet élément n'est pas encore inséré dans le document
L'élément HTML contient le nœud textuel, mais cet élément n'est pas encore inséré dans le document

La dernière instruction insère alors le tout.

appendChild()retourne une référence (voir plus loin pour plus de détails) pointant sur l'objet qui vient d'être inséré. Cela peut servir dans le cas où vous n'avez pas déclaré de variable intermédiaire lors du processus de création de votre élément. Par exemple, le code suivant ne pose pas de problème :

var span = document.createElement('span');
document.body.appendChild(span);
span.innerHTML = 'Du texte en plus !';

En revanche, si vous retirez l'étape intermédiaire (la première ligne) pour gagner une ligne de code alors vous allez être embêté pour modifier le contenu :

document.body.appendChild(document.createElement('span'));
span.innerHTML = 'Du texte en plus !'; // La variable « span » n'existe plus… Le code plante.

La solution à ce problème est d'utiliser la référence retournée parappendChild()de la façon suivante :

var span = document.body.appendChild(document.createElement('span'));
span.innerHTML = 'Du texte en plus !'; // Là, tout fonctionne !

Notions sur les références

En JavaScript et comme dans beaucoup de langages, le contenu des variables est « passé par valeur ». Cela veut donc dire que si une variablenick1contient le prénom « Clarisse » et qu'on affecte cette valeur à une autre variable, la valeur est copiée dans la nouvelle. On obtient alors deux variables distinctes, contenant la même valeur :

var nick1 = 'Clarisse';
var nick2 = nick1;

Si on modifie la valeur denick2, la valeur denick1reste inchangée : normal, les deux variables sont bien distinctes.

Les références

Outre le « passage par valeur », le JavaScript possède un « passage par référence ». En fait, quand une variable est créée, sa valeur est mise en mémoire par l'ordinateur. Pour pouvoir retrouver cette valeur, elle est associée à une adresse que seul l'ordinateur connaît et manipule (on ne s'en occupe pas).

Quand on passe une valeur par référence, on transmet l'adresse de la valeur, ce qui va permettre d'avoir deux variables qui pointent sur une même valeur ! Malheureusement, un exemple théorique d'un passage par référence n'est pas vraiment envisageable à ce stade du tutoriel, il faudra attendre d'aborder le chapitre sur la création d'objets. Cela dit, quand on manipule une page Web avec le DOM, on est confronté à des références, tout comme dans le chapitre suivant sur les événements.

Les références avec le DOM

Schématiser le concept de référence avec le DOM est assez simple : deux variables peuvent accéder au même élément. Regardez cet exemple :

var newLink = document.createElement('a');
var newLinkText = document.createTextNode('Le Site du Zéro');
newLink.id = 'sdz_link';
newLink.href = 'http://www.siteduzero.com';
newLink.appendChild(newLinkText);
document.getElementById('myP').appendChild(newLink);
// On récupère, via son ID, l'élément fraîchement inséré
var sdzLink = document.getElementById('sdz_link');
sdzLink.href = 'http://www.siteduzero.com/forum.html';
// newLink.href affiche bien la nouvelle URL :
alert(newLink.href);

La variablenewLinkcontient en réalité une référence vers l'élément<a>qui a été créé.newLinkne contient pas l'élément, il contient une adresse qui pointe vers ce fameux<a>. Une fois que l'élément HTML est inséré dans la page, on peut y accéder de nombreuses autres façons, comme avecgetElementById(). Quand on accède à un élément viagetElementById(), on le fait aussi au moyen d'une référence.

Ce qu'il faut retenir de tout ça, c'est que les objets du DOM sont toujours accessibles par référence, et c'est la raison pour laquelle ce code ne fonctionne pas :

var newDiv1 = document.createElement('div');
var newDiv2 = newDiv1; // On tente de copier le <div>

Eh oui, si vous avez tout suivi,newDiv2contient une référence qui pointe vers le même<div>quenewDiv1. Mais comment dupliquer un élément alors ? Eh bien il faut le cloner, et c'est ce que nous allons voir maintenant !

Cloner, remplacer, supprimer…

Cloner un élément

Pour cloner un élément, rien de plus simple :cloneNode(). Cette méthode requiert un paramètre booléen (trueoufalse) : si vous désirez cloner le nœud avec (true) ou sans (false) ses enfants et ses différents attributs.

Petit exemple très simple : on crée un élément<hr />, et on en veut un deuxième, donc on clone le premier :

// On va cloner un élément créé :
var hr1 = document.createElement('hr');
var hr2 = hr1.cloneNode(false); // Il n'a pas d'enfants…
// Ici, on clone un élément existant :
var paragraph1 = document.getElementById('myP');
var paragraph2 = paragraph1.cloneNode(true);
// Et attention, l'élément est cloné, mais pas « inséré » tant que l'on n'a pas appelé appendChild() :
paragraph1.parentNode.appendChild(paragraph2);

Essayer le code

Remplacer un élément par un autre

Pour remplacer un élément par un autre, rien de plus simple, il y areplaceChild(). Cette méthode accepte deux paramètres : le premier est le nouvel élément, et le deuxième est l'élément à remplacer. Tout commecloneNode(), cette méthode s'utilise sur tous les types de nœuds (éléments, nœuds textuels, etc.).

Dans l'exemple suivant, le contenu textuel (pour rappel, il s'agit du premier enfant de<a>) du lien va être remplacé par un autre. La méthodereplaceChild()est exécutée sur l'élément<a>, c'est-à-dire le nœud parent du nœud à remplacer.

<body>
<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
<script>
var link = document.querySelector('a');
var newLabel = document.createTextNode('et un hyperlien');
link.replaceChild(newLabel, link.firstChild);
</script>
</body>

Essayer le code

Supprimer un élément

Pour insérer un élément, on utiliseappendChild(), et pour en supprimer un, on utiliseremoveChild(). Cette méthode prend en paramètre le nœud enfant à retirer. Si on se calque sur le code HTML de l'exemple précédent, le script ressemble à ceci :

var link = document.querySelector('a');
link.parentNode.removeChild(link);

À noter que la méthoderemoveChild()retourne l'élément supprimé, ce qui veut dire qu'il est parfaitement possible de supprimer un élément HTML pour ensuite le réintégrer où on le souhaite dans le DOM :

var link = document.querySelector('a');
var oldLink = link.parentNode.removeChild(link); // On supprime l'élément et on le garde en stock
document.body.appendChild(oldLink); // On réintègre ensuite l'élément supprimé où on veut et quand on veut

Autres actions

Vérifier la présence d'éléments enfants

Rien de plus facile pour vérifier la présence d'éléments enfants :hasChildNodes(). Il suffit d'utiliser cette méthode sur l'élément de votre choix ; si cet élément possède au moins un enfant, la méthode renverratrue:

<div>
<p id="myP">Un peu de texte <a>et un lien</a></p>
</div>
<script>
var paragraph = document.querySelector('p');
alert(paragraph.hasChildNodes()); // Affiche true
</script>

Insérer à la bonne place :insertBefore()

La méthodeinsertBefore()permet d'insérer un élément avant un autre. Elle reçoit deux paramètres : le premier est l'élément à insérer, tandis que le deuxième est l'élément avant lequel l'élément va être inséré. Exemple :

<p id="myP">Un peu de texte <a>et un lien</a></p>
<script>
var paragraph = document.querySelector('p');
var emphasis = document.createElement('em'),
emphasisText = document.createTextNode(' en emphase légère ');
emphasis.appendChild(emphasisText);
paragraph.insertBefore(emphasis, paragraph.lastChild);
</script>

Essayer le code

Une bonne astuce :insertAfter()

Le JavaScript met à dispositioninsertBefore(), mais pasinsertAfter(). C'est dommage car, bien que l'on puisse s'en passer, cela est parfois assez utile. Qu'à cela ne tienne, créons donc une telle fonction.

Malheureusement, il ne nous est pas possible, à ce stade-ci du cours, de créer une méthode, qui s'appliquerait comme ceci :

element.insertAfter(newElement, afterElement)

Non, il va falloir nous contenter d'une « simple » fonction :

insertAfter(newElement, afterElement)
Algorithme

Pour insérer après un élément, on va d'abord récupérer l'élément parent. C'est logique, puisque l'insertion de l'élément va se faire soit viaappendChild(), soit viainsertBefore(): si on veut ajouter notre élément après le dernier enfant, c'est simple, il suffit d'appliquerappendChild(). Par contre, si l'élément après lequel on veut insérer notre élément n'est pas le dernier, on va utiliserinsertBefore()en ciblant l'enfant suivant, avecnextSibling:

function insertAfter(newElement, afterElement) {
var parent = afterElement.parentNode;
if (parent.lastChild === afterElement) { // Si le dernier élément est le même que l'élément après lequel on veut insérer, il suffit de faire appendChild()
parent.appendChild(newElement);
} else { // Dans le cas contraire, on fait un insertBefore() sur l'élément suivant
parent.insertBefore(newElement, afterElement.nextSibling);
}
}

Mini-TP : recréer une structure DOM

Afin de s’entraîner à jouer avec le DOM, voici quatre petits exercices. Pour chacun d'eux, une structure DOM sous forme de code HTML vous est donnée, et il vous est demandé de recréer cette structure en utilisant le DOM.

Premier exercice

Énoncé

Pour ce premier exercice, nous vous proposons de recréer « du texte » mélangé à divers éléments tels des<a>et des<strong>. C'est assez simple, mais pensez bien à ne pas vous emmêler les pinceaux avec tous les nœuds textuels !

<div id="divTP1">
Le <strong>World Wide Web Consortium</strong>, abrégé par le sigle <strong>W3C</strong>, est un
<a href="http://fr.wikipedia.org/wiki/Organisme_de_normalisation" title="Organisme de normalisation">organisme de standardisation</a> à but non-lucratif chargé de promouvoir la compatibilité des technologies du <a href="http://fr.wikipedia.org/wiki/World_Wide_Web" title="World Wide Web">World Wide Web</a>.
</div>


Corrigé
Le schéma du DOM du premier exercice
// On crée l'élément conteneur
var mainDiv = document.createElement('div');
mainDiv.id = 'divTP1';
// On crée tous les nœuds textuels, pour plus de facilité
var textNodes = [
document.createTextNode('Le '),
document.createTextNode('World Wide Web Consortium'),
document.createTextNode(', abrégé par le sigle '),
document.createTextNode('W3C'),
document.createTextNode(', est un '),
document.createTextNode('organisme de standardisation'),
document.createTextNode(' à but non-lucratif chargé de promouvoir la compatibilité des technologies du '),
document.createTextNode('World Wide Web'),
document.createTextNode('.')
];
// On crée les deux <strong> et les deux <a>
var w3cStrong1 = document.createElement('strong');
var w3cStrong2 = document.createElement('strong');
w3cStrong1.appendChild(textNodes[1]);
w3cStrong2.appendChild(textNodes[3]);
var orgLink = document.createElement('a');
var wwwLink = document.createElement('a');
orgLink.href = 'http://fr.wikipedia.org/wiki/Organisme_de_normalisation';
orgLink.title = 'Organisme de normalisation';
orgLink.appendChild(textNodes[5]);
wwwLink.href = 'http://fr.wikipedia.org/wiki/World_Wide_Web';
wwwLink.title = 'World Wide Web';
wwwLink.appendChild(textNodes[7]);
// On insère le tout dans mainDiv
mainDiv.appendChild(textNodes[0]);
mainDiv.appendChild(w3cStrong1);
mainDiv.appendChild(textNodes[2]);
mainDiv.appendChild(w3cStrong2);
mainDiv.appendChild(textNodes[4]);
mainDiv.appendChild(orgLink);
mainDiv.appendChild(textNodes[6]);
mainDiv.appendChild(wwwLink);
mainDiv.appendChild(textNodes[8]);
// On insère mainDiv dans le <body>
document.body.appendChild(mainDiv);

Essayer le code

Par mesure de facilité, tous les nœuds textuels sont contenus dans le tableautextNodes, ça évite de faire 250 variables différentes. Une fois les nœuds textuels créés, on crée les éléments<a>et<strong>. Une fois que tout cela est fait, on insère le tout, un élément après l'autre, dans ledivconteneur.

Deuxième exercice

Énoncé
<div id="divTP2">
<p>Langages basés sur ECMAScript :</p>
<ul>
<li>JavaScript</li>
<li>JScript</li>
<li>ActionScript</li>
<li>EX4</li>
</ul>
</div>

On ne va tout de même pas créer quatre éléments<li>« à la main »… Utilisez une bouclefor! Et souvenez-vous, utilisez un tableau pour définir les éléments textuels.


Corrigé
Le schéma du DOM du deuxième exercice
// On crée l'élément conteneur
var mainDiv = document.createElement('div');
mainDiv.id = 'divTP2';
// On crée tous les nœuds textuels, pour plus de facilité
var languages = [
document.createTextNode('JavaScript'),
document.createTextNode('JScript'),
document.createTextNode('ActionScript'),
document.createTextNode('EX4')
];
// On crée le paragraphe
var paragraph = document.createElement('p');
var paragraphText = document.createTextNode('Langages basés sur ECMAScript :');
paragraph.appendChild(paragraphText);
// On crée la liste, et on boucle pour ajouter chaque item
var uList = document.createElement('ul'),
uItem;
for (var i = 0, c = languages.length; i < c; i++) {
uItem = document.createElement('li');
uItem.appendChild(languages[i]);
uList.appendChild(uItem);
}
// On insère le tout dans mainDiv
mainDiv.appendChild(paragraph);
mainDiv.appendChild(uList);
// On insère mainDiv dans le <body>
document.body.appendChild(mainDiv);

Essayer le code

Les nœuds textuels de la liste à puces sont créés par le biais du tableaulanguages, et pour créer chaque élément<li>, il suffit de boucler sur le nombre d'items du tableau.

Troisième exercice

Énoncé

Voici une version légèrement plus complexe de l'exercice précédent. Le schéma de fonctionnement est le même, mais ici le tableaulanguagescontiendra des objets littéraux , et chacun de ces objets contiendra deux propriétés : le nœud du<dt>et le noeud du<dd>.

<div id="divTP3">
<p>Langages basés sur ECMAScript :</p>
<dl>
<dt>JavaScript</dt>
<dd>JavaScript est un langage de programmation de scripts principalement utilisé dans les pages web interactives mais aussi coté serveur.</dd>
<dt>JScript</dt>
<dd>JScript est le nom générique de plusieurs implémentations d'ECMAScript 3 créées par Microsoft.</dd>
<dt>ActionScript</dt>
<dd>ActionScript est le langage de programmation utilisé au sein d'applications clientes (Adobe Flash, Adobe Flex) et serveur (Flash media server, JRun, Macromedia Generator).</dd>
<dt>EX4</dt>
<dd>ECMAScript for XML (E4X) est une extension XML au langage ECMAScript.</dd>
</dl>
</div>


Corrigé
Le schéma du DOM du troisième exercice
// On crée l'élément conteneur
var mainDiv = document.createElement('div');
mainDiv.id = 'divTP3';
// On place le texte dans des objets, eux-mêmes placés dans un tableau
// Par facilité, la création des nœuds textuels se fera dans la boucle
var languages = [{
t: 'JavaScript',
d: 'JavaScript est un langage de programmation de scripts principalement utilisé dans les pages web interactives mais aussi coté serveur.'
}, {
t: 'JScript',
d: 'JScript est le nom générique de plusieurs implémentations d\'ECMAScript 3 créées par Microsoft.'
}, {
t: 'ActionScript',
d: 'ActionScript est le langage de programmation utilisé au sein d\'applications clientes (Adobe Flash, Adobe Flex) et serveur (Flash media server, JRun, Macromedia Generator).'
}, {
t: 'EX4',
d: 'ECMAScript for XML (E4X) est une extension XML au langage ECMAScript.'
}];
// On crée le paragraphe
var paragraph = document.createElement('p');
var paragraphText = document.createTextNode('Langages basés sur ECMAScript :');
paragraph.appendChild(paragraphText);
// On crée la liste, et on boucle pour ajouter chaque item
var defList = document.createElement('dl'),
defTerm, defTermText,
defDefn, defDefnText;
for (var i = 0, c = languages.length; i < c; i++) {
defTerm = document.createElement('dt');
defDefn = document.createElement('dd');
defTermText = document.createTextNode(languages[i].t);
defDefnText = document.createTextNode(languages[i].d);
defTerm.appendChild(defTermText);
defDefn.appendChild(defDefnText);
defList.appendChild(defTerm);
defList.appendChild(defDefn);
}
// On insère le tout dans mainDiv
mainDiv.appendChild(paragraph);
mainDiv.appendChild(defList);
// On insère mainDiv dans le <body>
document.body.appendChild(mainDiv);

Le tableau contient des objets comme ceci :

{
t: 'Terme',
d: 'Définition'}

Essayer le code

Créer une liste de définitions (<dl>) n'est pas plus compliqué qu'une liste à puces normale, la seule chose qui diffère est que<dt>et<dd>sont ajoutés conjointement au sein de la boucle.

Quatrième exercice

Énoncé

Un rien plus corsé… quoique. Ici, la difficulté réside dans le nombre important d'éléments à imbriquer les uns dans les autres. Si vous procédez méthodiquement, vous avez peu de chance de vous planter.

<div id="divTP4">
<form enctype="multipart/form-data" method="post" action="upload.php">
<fieldset>
<legend>Uploader une image</legend>
<div style="text-align: center">
<label for="inputUpload">Image à uploader :</label>
<input type="file" name="inputUpload" id="inputUpload" />
<br /><br />
<input type="submit" value="Envoyer" />
</div>
</fieldset>
</form>
</div>


Corrigé
Le schéma du DOM du quatrième exercice
// On crée l'élément conteneur
var mainDiv = document.createElement('div');
mainDiv.id = 'divTP4';
// Création de la structure du formulaire
var form = document.createElement('form');
var fieldset = document.createElement('fieldset');
var legend = document.createElement('legend'),
legendText = document.createTextNode('Uploader une image');
var center = document.createElement('div');
form.action = 'upload.php';
form.enctype = 'multipart/form-data';
form.method = 'post';
center.setAttribute('style', 'text-align: center');
legend.appendChild(legendText);
fieldset.appendChild(legend);
fieldset.appendChild(center);
form.appendChild(fieldset);
// Création des champs
var label = document.createElement('label'),
labelText = document.createTextNode('Image à uploader :');
var input = document.createElement('input');
var br = document.createElement('br');
var submit = document.createElement('input');
input.type = 'file';
input.id = 'inputUpload';
input.name = input.id;
submit.type = 'submit';
submit.value = 'Envoyer';
label.htmlFor = 'inputUpload';
label.appendChild(labelText);
center.appendChild(label);
center.appendChild(input);
center.appendChild(br);
center.appendChild(br.cloneNode(false)); // On clone, pour mettre un deuxième <br />
center.appendChild(submit);
// On insère le formulaire dans mainDiv
mainDiv.appendChild(form);
// On insère mainDiv dans le <body>
document.body.appendChild(mainDiv);

Essayer le code

Comme il y a beaucoup d'éléments à créer, pourquoi ne pas diviser le script en deux : la structure du formulaire, et les champs. C'est plus propre, et on s'y retrouve mieux.

Conclusion des TP

Il est très probable que vous n'ayez pas organisé votre code comme dans les corrections, ou que vous n'ayez pas utilisé les mêmes idées, comme utiliser un tableau, ou même un tableau d'objets. Votre code est certainement bon, mais retenez une chose : essayez d'avoir un code clair et propre, tout en étant facile à comprendre, cela vous simplifiera la tâche !

En résumé
  • Une fois qu'on a accédé à un élément, on peut naviguer vers d'autres éléments avecparentNode,previousSiblingetnextSibling, ainsi que récupérer des informations sur le nom des éléments et leur contenu.

  • Pour ajouter un élément, il faut d'abord le créer, puis lui adjoindre des attributs et enfin l'insérer à l'endroit voulu au sein du document.

  • Outre le « passage par valeur », le JavaScript possède un « passage par référence » qui est fréquent lorsqu'on manipule le DOM. C'est cette histoire de référence qui nous oblige à utiliser une méthode telle quecloneNode()pour dupliquer un élément. En effet, copier la variable qui pointe vers cet élément ne sert à rien.

  • SiappendChild()est particulièrement pratique,insertBefore()l'est tout autant pour insérer un élément avant un autre. Créer une fonctioninsertAfter()est assez simple et peut faire gagner du temps.

  1. Q.C.M.

  2. Questionnaire fléché

  3. Insérer des éléments dans une liste

  4. Modifier un tableau

  5. Remplacer un élément par un autre

  6. Supprimer les balises<br />

  7. Supprimer tous les enfants

  8. Écrire une fonction de création d'éléments