Fil d'Ariane
Mis à jour le mercredi 5 juillet 2017
  • 40 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Manipuler le code HTML (partie 1/2)

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

Dans ce premier chapitre consacré à la manipulation du code HTML, nous allons voir le concept du DOM. Nous étudierons tout d'abord comment naviguer entre les différents nœuds qui composent une page HTML puis nous aborderons l'édition du contenu d'une page en ajoutant, modifiant et supprimant des nœuds.

Vous allez rapidement constater qu'il est plutôt aisé de manipuler le contenu d'une page web et que cela va vous devenir indispensable par la suite.

Le Document Object Model

Le Document Object Model (abrégé DOM) est une interface de programmation pour les documents XML et HTML.

Le DOM est donc une API qui s'utilise avec les documents XML et HTML, et qui va nous permettre, via le JavaScript, d'accéder au code XML et/ou HTML d'un document. C'est grâce au DOM que nous allons pouvoir modifier des éléments HTML (afficher ou masquer un <div> par exemple), en ajouter, en déplacer ou même en supprimer.

Petit historique

À l'origine, quand le JavaScript a été intégré dans les premiers navigateurs (Internet Explorer et Netscape Navigator), le DOM n'était pas unifié, c'est-à-dire que les deux navigateurs possédaient un DOM différent. Et donc, pour accéder à un élément HTML, la manière de faire différait d'un navigateur à l'autre, ce qui obligeait les développeurs Web à coder différemment en fonction du navigateur. En bref, c'était un peu la jungle.

Le W3C a mis de l'ordre dans tout ça, et a publié une nouvelle spécification que nous appellerons « DOM-1 » (pour DOM Level 1). Cette nouvelle spécification définit clairement ce qu'est le DOM, et surtout comment un document HTML ou XML est schématisé. Depuis lors, un document HTML ou XML est représenté sous la forme d'un arbre, ou plutôt hiérarchiquement. Ainsi, l'élément <html> contient deux éléments enfants : <head> et <body>, qui à leur tour contiennent d'autres éléments enfants.

Ensuite, la spécification DOM-2 a été publiée. La grande nouveauté de cette version 2 est l'introduction de la méthode getElementById() qui permet de récupérer un élément HTML ou XML en connaissant son ID.

L'objet window

Avant de véritablement parler du document, c'est-à-dire de la page Web, nous allons parler de l'objet window. L'objet window est ce qu'on appelle un objet global qui représente la fenêtre du navigateur. C'est à partir de cet objet que le JavaScript est exécuté.

Si nous reprenons notre « Hello World! » du début, nous avons :

<!DOCTYPE html>
<html>
<head>
    <title>Hello World!</title>
</head>

<body>

    <script>

        alert('Hello world!');

    </script>

</body>
</html>

Contrairement à ce qui a été dit dans ce cours, alert() n'est pas vraiment une fonction. Il s'agit en réalité d'une méthode appartenant à l'objet window. Mais l'objet window est dit implicite, c'est-à-dire qu'il n'y a généralement pas besoin de le spécifier. Ainsi, ces deux instructions produisent le même effet, à savoir ouvrir une boîte de dialogue :

window.alert('Hello world!');
alert('Hello world!');

Puisqu'il n'est pas nécessaire de spécifier l'objet window, on ne le fait généralement pas sauf si cela est nécessaire, par exemple si on manipule des frames.

De même, lorsque vous déclarez une variable dans le contexte global de votre script, cette variable deviendra en vérité une propriété de l'objet window. Afin de vous démontrer facilement la chose, regardez donc ceci :

var text = 'Variable globale !';

(function() { // On utilise une IIFE pour « isoler » du code

    var text = 'Variable locale !';

    alert(text); // Forcément, la variable locale prend le dessus

    alert(window.text); // Mais il est toujours possible d'accéder à la variable globale grâce à l'objet « window »

})();

Une dernière chose importante qu'il vous faut mémoriser : toute variable non déclarée (donc utilisée sans jamais écrire le mot-clé var) deviendra immédiatement une propriété de l'objet window, et ce, quel que soit l'endroit où vous utilisez cette variable ! Prenons un exemple simple :

(function() { // On utilise une IIFE pour « isoler » du code

    text = 'Variable accessible !'; // Cette variable n'a jamais été déclarée et pourtant on lui attribue une valeur

})();

alert(text); // Affiche : « Variable accessible ! »

Notre variable a été utilisée pour la première fois dans une IIFE et pourtant nous y avons accès depuis l'espace global ! Alors pourquoi cela fonctionne-t-il de cette manière ? Tout simplement parce que le JavaScript va chercher à résoudre le problème que nous lui avons donné : on lui demande d'attribuer une valeur à la variable text, il va donc chercher cette variable mais ne la trouve pas, la seule solution pour résoudre le problème qui lui est donné est alors d'utiliser l'objet window. Ce qui veut dire qu'en écrivant :

text = 'Variable accessible !';

le code sera alors interprété de cette manière si aucune variable accessible n'existe avec ce nom :

window.text = 'Variable accessible !';

Si nous vous montrons cette particularité du JavaScript c'est pour vous conseiller de ne pas vous en servir ! Si vous n'utilisez jamais le mot-clé var alors vous allez très vite arriver à de grandes confusions dans votre code (et à de nombreux bugs). Si vous souhaitez déclarer une variable dans l'espace global alors que vous vous trouvez actuellement dans un autre espace (une IIFE, par exemple), spécifiez donc explicitement l'objet window. Le reste du temps, pensez bien à écrire le mot-clé var.

Le document

L'objet document est un sous-objet de window, l'un des plus utilisés. Et pour cause, il représente la page Web et plus précisément la balise <html>. C'est grâce à cet élément-là que nous allons pouvoir accéder aux éléments HTML et les modifier. Voyons donc, dans la sous-partie suivante, comment naviguer dans le document.

Naviguer dans le document

La structure DOM

Comme il a été dit précédemment, le DOM pose comme concept que la page Web est vue comme un arbre, comme une hiérarchie d'éléments. On peut donc schématiser une page Web simple comme ceci :

Une page Web peut être vue comme un arbre
Une page Web peut être vue comme un arbre

Voici le code source de la page :

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Le titre de la page</title>
</head>

<body>
    <div>
        <p>Un peu de texte <a>et un lien</a></p>
    </div>
</body>
</html>

Le schéma est plutôt simple : l'élément <html> contient deux éléments, appelés enfants : <head> et <body>. Pour ces deux enfants, <html> est l'élément parent. Chaque élément est appelé nœud (node en anglais). L'élément <head> contient lui aussi deux enfants : <meta> et <title>. <meta> ne contient pas d'enfant tandis que <title> en contient un, qui s’appelle #text. Comme son nom l'indique, #text est un élément qui contient du texte.

Il est important de bien saisir cette notion : le texte présent dans une page Web est vu par le DOM comme un nœud de type #text. Dans le schéma précédent, l'exemple du paragraphe qui contient du texte et un lien illustre bien cela :

<p>
    Un peu de texte
    <a>et un lien</a>
</p>

Si on va à la ligne après chaque nœud, on remarque clairement que l'élément <p> contient deux enfants : #text qui contient « Un peu de texte » et <a>, qui lui-même contient un enfant #text représentant « et un lien ».

Accéder aux éléments

L'accès aux éléments HTML via le DOM est assez simple mais demeure actuellement plutôt limité. L'objet document possède trois méthodes principales : getElementById(), getElementsByTagName() et getElementsByName().

getElementById()

Cette méthode permet d'accéder à un élément en connaissant son ID qui est simplement l'attribut id de l'élément. Cela fonctionne de cette manière :

<div id="myDiv">
    <p>Un peu de texte <a>et un lien</a></p>
</div>

<script>
    var div = document.getElementById('myDiv');

    alert(div);
</script>

Essayer le code

En exécutant ce code, le navigateur affiche ceci :

Notre div est bien un objet de type HTMLDivElement
Notre div est bien un objet de type HTMLDivElement

Il nous dit que div est un objet de type HTMLDivElement. En clair, c'est un élément HTML qui se trouve être un <div>, ce qui nous montre que le script fonctionne correctement.

getElementsByTagName()

Cette méthode permet de récupérer, sous la forme d'un tableau, tous les éléments de la famille. Si, dans une page, on veut récupérer tous les <div>, il suffit de faire comme ceci :

var divs = document.getElementsByTagName('div');

for (var i = 0, c = divs.length ; i < c ; i++) {
    alert('Element n° ' + (i + 1) + ' : ' + divs[i]);
}

Essayer le code

La méthode retourne une collection d'éléments (utilisable de la même manière qu'un tableau). Pour accéder à chaque élément, il est nécessaire de parcourir le tableau avec une petite boucle.

Deux petites astuces :

  1. Cette méthode est accessible sur n'importe quel élément HTML et pas seulement sur l'objet document.

  2. En paramètre de cette méthode vous pouvez mettre une chaîne de caractères contenant un astérisque * qui récupérera tous les éléments HTML contenus dans l'élément ciblé.

getElementsByName()

Cette méthode est semblable à getElementsByTagName() et permet de ne récupérer que les éléments qui possèdent un attribut name que vous spécifiez. L'attribut name n'est utilisé qu'au sein des formulaires, et est déprécié depuis la spécification HTML5 dans tout autre élément que celui d'un formulaire. Par exemple, vous pouvez vous en servir pour un élément <input> mais pas pour un élément <map>.

Sachez aussi que cette méthode est dépréciée en XHTML mais est standardisée en HTML5.

Accéder aux éléments grâce aux technologies récentes

Ces dernières années, le JavaScript a beaucoup évolué pour faciliter le développement Web. Les deux méthodes que nous allons étudier sont récentes et ne sont pas supportées par les très vieilles versions des navigateurs, leur support commence à partir de la version 8 d'Internet Explorer, pour les autres navigateurs vous n'avez normalement pas de soucis à vous faire.

Ces deux méthodes sont querySelector() et querySelectorAll() et ont pour particularité de grandement simplifier la sélection d'éléments dans l'arbre DOM grâce à leur mode de fonctionnement. Ces deux méthodes prennent pour paramètre un seul argument : une chaîne de caractères !

Cette chaîne de caractères doit être un sélecteur CSS comme ceux que vous utilisez dans vos feuilles de style. Exemple :

#menu .item span

Ce sélecteur CSS stipule que l'on souhaite sélectionner les balises de type <span> contenues dans les classes .item elles-mêmes contenues dans un élément dont l'identifiant est #menu.

Le principe est plutôt simple mais très efficace. Sachez que ces deux méthodes supportent aussi les sélecteurs CSS 3, bien plus complets ! Vous pouvez consulter leur liste sur la spécification du W3C.

Voyons maintenant les particularités de ces deux méthodes. La première, querySelector(), renvoie le premier élément trouvé correspondant au sélecteur CSS, tandis que querySelectorAll() va renvoyer tous les éléments (sous forme de tableau) correspondant au sélecteur CSS fourni. Prenons un exemple simple :

<div id="menu">

    <div class="item">
        <span>Élément 1</span>
        <span>Élément 2</span>
    </div>

    <div class="publicite">
        <span>Élément 3</span>
        <span>Élément 4</span>
    </div>

</div>

<div id="contenu">
    <span>Introduction au contenu de la page...</span>
</div>

Maintenant, essayons le sélecteur CSS présenté plus haut : #menu .item span

var query = document.querySelector('#menu .item span'),
    queryAll = document.querySelectorAll('#menu .item span');

alert(query.innerHTML); // Affiche : "Élément 1"

alert(queryAll.length); // Affiche : "2"
alert(queryAll[0].innerHTML + ' - ' + queryAll[1].innerHTML); // Affiche : "Élément 1 - Élément 2"

Nous obtenons bien les résultats escomptés ! Nous vous conseillons de bien vous rappeler ces deux méthodes. Elles sont déjà utiles sur des projets voués à tourner sur des navigateurs récents, et d'ici à quelques années elles pourraient bien devenir habituelles (le temps que les vieilles versions des navigateurs disparaissent pour de bon).

L'héritage des propriétés et des méthodes

Le JavaScript voit les éléments HTML comme étant des objets, cela veut donc dire que chaque élément HTML possède des propriétés et des méthodes. Cependant faites bien attention parce que tous ne possèdent pas les mêmes propriétés et méthodes. Certaines sont néanmoins communes à tous les éléments HTML, car tous les éléments HTML sont d'un même type : le type Node, qui signifie « nœud » en anglais.

Notion d'héritage

Nous avons vu qu'un élément <div> est un objet HTMLDivElement, mais un objet, en JavaScript, peut appartenir à différents groupes. Ainsi, notre <div> est un HTMLDivElement, qui est un sous-objet d'HTMLElement qui est lui-même un sous-objet d'Element. Element est enfin un sous-objet de Node. Ce schéma est plus parlant :

En Javascript, un objet peut appartenir à plusieurs groupes
En JavaScript, un objet peut appartenir à plusieurs groupes

L'objet Node apporte un certain nombre de propriétés et de méthodes qui pourront être utilisées depuis un de ses sous-objets. En clair, les sous-objets héritent des propriétés et méthodes de leurs objets parents. Voilà donc ce que l'on appelle l'héritage.

Éditer les éléments HTML

Maintenant que nous avons vu comment accéder à un élément, nous allons voir comment l'éditer. Les éléments HTML sont souvent composés d'attributs (l'attribut href d'un <a> par exemple), et d'un contenu, qui est de type #text. Le contenu peut aussi être un autre élément HTML.

Comme dit précédemment, un élément HTML est un objet qui appartient à plusieurs objets, et de ce fait, qui hérite des propriétés et méthodes de ses objets parents.

Les attributs

Via l'objet Element

Pour interagir avec les attributs, l'objet Element nous fournit deux méthodes, getAttribute() et setAttribute() permettant respectivement de récupérer et d'éditer un attribut. Le premier paramètre est le nom de l'attribut, et le deuxième, dans le cas de setAttribute() uniquement, est la nouvelle valeur à donner à l'attribut. Petit exemple :

<body>
    <a id="myLink" href="http://www.un_lien_quelconque.com">Un lien modifié dynamiquement</a>

    <script>
        var link = document.getElementById('myLink');
        var href = link.getAttribute('href'); // On récupère l'attribut « href »

        alert(href);

        link.setAttribute('href', 'http://www.siteduzero.com'); // On édite l'attribut « href »
    </script>
</body>

On commence par récupérer l'élément myLink, et on lit son attribut href via getAttribute(). Ensuite on modifie la valeur de l'attribut href avec setAttribute(). Le lien pointe maintenant vers http://www.siteduzero.com.

Les attributs accessibles

En fait, pour la plupart des éléments courants comme <a>, il est possible d'accéder à un attribut via une propriété. Ainsi, si on veut modifier la destination d'un lien, on peut utiliser la propriété href, comme ceci :

<body>
    <a id="myLink" href="http://www.un_lien_quelconque.com">Un lien modifié dynamiquement</a>

    <script>
        var link = document.getElementById('myLink');
        var href = link.href;

        alert(href);

        link.href = 'http://www.siteduzero.com';
    </script>
</body>

Essayer le code

C'est cette façon de faire que l'on utilisera majoritairement pour les formulaires : pour récupérer ou modifier la valeur d'un champ, on utilisera la propriété value.

La classe

On peut penser que pour modifier l'attribut class d'un élément HTML, il suffit d'utiliser element.class. Ce n'est pas possible, car le mot-clé class est réservé en JavaScript, bien qu'il n'ait aucune utilité. À la place de class, il faudra utiliser className.

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Le titre de la page</title>
    <style>
        .blue {
            background: blue;
            color: white;
        }
    </style>
</head>

<body>
    <div id="myColoredDiv">
        <p>Un peu de texte <a>et un lien</a></p>
    </div>

    <script>
        document.getElementById('myColoredDiv').className = 'blue';
    </script>
</body>
</html>

Dans cet exemple, on définit la classe CSS .blue à l'élément myColoredDiv, ce qui fait que cet élément sera affiché avec un arrière-plan bleu et un texte blanc.

Faites attention : si votre élément comporte plusieurs classes (exemple : <a class="external red u">) et que vous récupérez la classe avec className, cette propriété ne retournera pas un tableau avec les différentes classes, mais bien la chaîne « external red u », ce qui n'est pas vraiment le comportement souhaité. Il vous faudra alors couper cette chaîne avec la méthode split() pour obtenir un tableau, comme ceci :

var classes = document.getElementById('myLink').className;
var classesNew = [];
classes = classes.split(' ');

for (var i = 0, c = classes.length; i < c; i++) {
    if (classes[i]) {
        classesNew.push(classes[i]);
    }
}

alert(classesNew);

Essayer le code

Là, on récupère les classes, on découpe la chaîne, mais comme il se peut que plusieurs espaces soient présents entre chaque nom de classe, on vérifie chaque élément pour voir s'il contient quelque chose (s'il n'est pas vide). On en profite pour créer un nouveau tableau, classesNew, qui contiendra les noms des classes, sans « parasites ».

Si le support d'Internet Explorer avant sa version 10 vous importe peu, vous pouvez aussi vous tourner vers la propriété classList  qui permet de consulter les classes sous forme d'un tableau et de les manipuler aisément :

var div = document.querySelector('div');

// Ajoute une nouvelle classe
div.classList.add('new-class');

// Retire une classe
div.classList.remove('new-class');

// Retire une classe si elle est présente ou bien l'ajoute si elle est absente
div.classList.toggle('toggled-class');

// Indique si une classe est présente ou non
if (div.classList.contains('old-class')) {
    alert('La classe .old-class est présente !');
}

// Parcourt et affiche les classes CSS
var result = '';

for (var i = 0; i < div.classList.length; i++) {
    result += '.' + div.classList[i] + '\n';
}

alert(result);

Le contenu : innerHTML

La propriété innerHTML est spéciale et demande une petite introduction. Elle a été créée par Microsoft pour les besoins d'Internet Explorer et a été normalisée au sein du HTML5. Bien que non normalisée pendant des années, elle est devenue un standard parce que tous les navigateurs la supportaient déjà, et non l'inverse comme c'est généralement le cas.

Récupérer du HTML

innerHTML permet de récupérer le code HTML enfant d'un élément sous forme de texte. Ainsi, si des balises sont présentes, innerHTML les retournera sous forme de texte :

<body>
    <div id="myDiv">
        <p>Un peu de texte <a>et un lien</a></p>
    </div>

    <script>
        var div = document.getElementById('myDiv');

        alert(div.innerHTML);
    </script>
</body>

Nous avons donc bien une boîte de dialogue qui affiche le contenu de myDiv, sous forme de texte :

Le contenu de myDiv est bien affiché
Le contenu de myDiv est bien affiché
Ajouter ou éditer du HTML

Pour éditer ou ajouter du contenu HTML, il suffit de faire l'inverse, c'est-à-dire de définir un nouveau contenu :

document.getElementById('myDiv').innerHTML = '<blockquote>Je mets une citation à la place du paragraphe</blockquote>';

Si vous voulez ajouter du contenu, et ne pas modifier le contenu déjà en place, il suffit d’utiliser += à la place de l'opérateur d'affectation :

document.getElementById('myDiv').innerHTML += ' et <strong>une portion mise en emphase</strong>.';

Toutefois, une petite mise en garde : il ne faut pas utiliser le += dans une boucle ! En effet, innerHTML ralentit considérablement l'exécution du code si l'on opère de cette manière, il vaut donc mieux concaténer son texte dans une variable pour ensuite ajouter le tout via innerHTML. Exemple :

var text = '';

while ( /* condition */ ) {
    text += 'votre_texte'; // On concatène dans la variable « text »
}

element.innerHTML = text; // Une fois la concaténation terminée, on ajoute le tout à « element » via innerHTML

innerText et textContent

Penchons-nous maintenant sur deux propriétés analogues à innerHTML : innerText pour Internet Explorer et textContent pour les autres navigateurs.

innerText

La propriété innerText a aussi été introduite dans Internet Explorer, mais à la différence de sa propriété sœur innerHTML, elle n'a jamais été standardisée et n'est pas supportée par tous les navigateurs. Internet Explorer (pour toute version antérieure à la neuvième) ne supporte que cette propriété et non pas la version standardisée que nous verrons par la suite.

Le fonctionnement d'innerText est le même qu'innerHTML excepté le fait que seul le texte est récupéré, et non les balises. C'est pratique pour récupérer du contenu sans le balisage, petit exemple :

<body>
    <div id="myDiv">
        <p>Un peu de texte <a>et un lien</a></p>
    </div>

    <script>
        var div = document.getElementById('myDiv');

        alert(div.innerText);
    </script>
</body>

Ce qui nous donne bien « Un peu de texte et un lien », sans les balises :

Le texte est affiché sans les balises HTML
Le texte est affiché sans les balises HTML

textContent

La propriété textContent est la version standardisée d'innerText ; elle est reconnue par tous les navigateurs à l'exception des versions d'Internet Explorer antérieures à la 9. Le fonctionnement est évidemment le même. Maintenant une question se pose : comment faire un script qui fonctionne à la fois pour Internet Explorer et les autres navigateurs ? C'est ce que nous allons voir !

Tester le navigateur

Il est possible via une simple condition de tester si le navigateur prend en charge telle ou telle méthode ou propriété.

<body>
    <div id="myDiv">
        <p>Un peu de texte <a>et un lien</a></p>
    </div>

    <script>
        var div = document.getElementById('myDiv');
        var txt = '';

        if (div.textContent) { // « textContent » existe ? Alors on s'en sert !
            txt = div.textContent;
        } else if (div.innerText) { // « innerText » existe ? Alors on doit être sous IE.
            txt = div.innerText + ' [via Internet Explorer]';
        } else { // Si aucun des deux n'existe, cela est sûrement dû au fait qu'il n'y a pas de texte
            txt = ''; // On met une chaîne de caractères vide
        }

        alert(txt);
    </script>
</body>

Il suffit donc de tester par le biais d'une condition si l'instruction fonctionne. Si textContent ne fonctionne pas, pas de soucis, on prend innerText :

La fenêtre s'affiche avec Internet Explorer
La fenêtre s'affiche avec Internet Explorer

Cela dit, ce code est quand même très long et redondant. Il est possible de le raccourcir de manière considérable :

txt = div.textContent || div.innerText || '';
En résumé
  • Le DOM va servir à accéder aux éléments HTML présents dans un document afin de les modifier et d'interagir avec eux.

  • L'objet window est un objet global qui représente la fenêtre du navigateur. document, quant à lui, est un sous-objet de window et représente la page Web. C'est grâce à lui que l'on va pouvoir accéder aux éléments HTML de la page Web.

  • Les éléments de la page sont structurés comme un arbre généalogique, avec l'élément <html> comme élément fondateur.

  • Différentes méthodes, comme getElementById(), getElementsByTagName(), querySelector() ou querySelectorAll(), sont disponibles pour accéder aux éléments.

  • Les attributs peuvent tous être modifiés grâce à setAttribute(). Certains éléments possèdent des propriétés qui permettent de modifier ces attributs.

  • La propriété innerHTML permet de récupérer ou de définir le code HTML présent à l'intérieur d'un élément.

  • De leur côté, textContent et innerText ne sont capables que de définir ou récupérer du texte brut, sans aucunes balises HTML.

Exemple de certificat de réussite
Exemple de certificat de réussite