Infoforall

Javascript 12 : Créer des balises

Cette activité va vous permettre de faire le point sur l'accès aux balises HTML depuis le code javascript.

Pourquoi faire cela ?

Si vous êtes certain de devoir afficher 8 éléments et jamais un de plus ou un de moins, nul besoin de faire ceci.

Par contre, si certains éléments ne sont pas nécéssairement présents en nombre constant, si l'utilisateur peut en faire apparaitre ou disparaitre certains, il est temps d'apprendre à le faire dynamiquement.

Or, par définition, une page HTML statique est ... statique. Il va donc falloir passer par javascript pour trouver des balises à la volée.

Un exemple pour la forme :

Vous allez voir que nous allons revoir beaucoup de choses plus ou moins connues puisque dans l'activité précédente nous avons créer des balises IMG. Or, IMG ou non, une balise reste une balise.

1 - Rappel des notions vues jusqu'à présent

Cette partie ne comporte aucune question : il s'agit juste de reprendre les notions importantes permettant d'accéder aux balises et d'en lire le contenu.

Nous allons revoir comment :

Et nous allons voir une nouvelle façon de faire ces lectures et écritures : les méthodes getAttribute pour la lecture et setAttribute pour l'écriture.

Vous savez qu'une page HTML est composée de balises (tags) qui s'imbriquent les unes dans les autres.

Une balise qui en contient d'autres est dite conteneur ou parent en javascript.

Une balise qui est contenue dans une autre autre balise est dite child (enfant en français).

1.1 Recherche par nom de balise/tag

Les balises possèdent toujours un nom (p, h3, div, span, head, ...).

On peut trouver la liste des balises ayant un certain type avec la méthode getElementsByTagName. Il y a un s à Elements : on obtient donc la collection / tableau / liste des noeuds, même si la collection ne contient qu'un seul élément.

Pour accéder aux différents noeuds des balises p de ma page, je peux utiliser la méthode basique :

var listeNoeud = document.getElementsByTagName("p");

var premierParagraphe = listeNoeud[0];

var deuxiemeParagraphe = listeNoeud[1];

Il faut donc se souvenir que le premier élement porte le numéro d'index 0.

On peut aussi utiliser les méthodes querySelector et querySelectorAll.

La première méthode renvoie directement UN noeud. Si plusieurs balises du même type existent, la méthode va renvoyer le noeud de la première qu'elle trouve dans l'ordre de la page HTML.

var premierParagrapheRencontre = document.querySelector("p");

Par contre, la seconde méthode va renvoyer la collection des balises. Elle fonctionne donc comme getElementsByTagName.

var listeNoeud = document.querySelectorAll("p");

var premierParagraphe = listeNoeud[0];

var deuxiemeParagraphe = listeNoeud[1];

1.2 Recherche par identifiant

Certaines balises peuvent possèder un attribut identifiant (id) unique. En CSS, on parvient à la rechercher en utilisant le dièse #.

On peut obtenir le noeud d'une balise ayant (dans le code HTML) un id = "ma_balise" à l'aide de :

1.3 Recherche par classe

Si vous voulez agir à l'identique sur plusieurs balises, il ne faut pas utiliser un identifiant mais une classe (class).

En CSS, on parvient à trouver les balises possèdant cette classe à l'aide du point . .

On peut obtenir la collection des noeuds des balises ayant (dans le code HTML) une class="ma_classe" à l'aide de :

Remarque : l'activité suivante vous montrera comment chercher avec encore plus de précision dans le DOM, le Document Object Model.

Maintenant que nous avons la référence du noeud d'une balise, nous allons pouvoir revoir ce que nous savons faire avec. A savoir :

1.4 Lecture des attributs

Nous prendrons ici le cas de la balise HTML suivante qui possède une balise-image de classe "affichage_image" :

<img class="affichage_image" src = "monImage.PNG" alt = "Texte de remplacement" title = "Texte du titre" width="628" height="535" />

Si la balise est courante et que son attribut visé est standard également, il existe certainement une propriété javascript portant le même nom que l'attribut html et qui permet d'accéder directement au contenu de celui-ci. Prenons l'exemple de l'attribut width des balises img :

var premiereImageTrouvee = document.querySelector(".affichage_image");

var laLargeur = premiereImageTrouvee.width ;

Sinon, vous pouvez toujours utiliser la méthode initialement faite pour cela : getAttribute.

Le code suivant va lire également le contenu de l'attribut width de la balise html. Mais c'est un peu plus long à taper :

var premiereImageTrouvee = document.querySelector(".affichage_image");

var laLargeur = premiereImageTrouvee.getAttribute("width") ;

Par contre, voici un exemple où la première recherche va échouer, là où la seconde va réussir : nous allons créer une balise p contenant un attribut totalement personnel : l'attribut lemien. D'ailleurs, cet attribut ne devrait pas obtenir de couleur particulière dans votre éditeur de texte : il ne s'agit absolument pas d'un attribut officiel.

<!DOCTYPE html>

<html>

    <p id="special" lemien="bizarre">Contenu suivant de la section.</p>

    <script>

        var mabalise = document.querySelector("#special");

        alert(mabalise.lemien);

        alert(mabalise.getAttribute("lemien"));

    </script>

</html>

Pour gagner du temps, j'ai inséré le script directement dans la page HTML.

On crée donc une balise paragraphe avec deux attributs : un id classique et un lemien totalement inconnu du navigateur qui va interpréter votre code.

On va chercher avec javascript le noeud du paragraphe.

La première alerte tente d'utiliser la propriété lemien qui, bien entendu, n'existe pas dans javascript. On a alors "undefined" en réponse.

La deuxième alerte utilise la méthode getAttribute et parvient bien à afficher le contenu de l'attribut lemien ("bizarre") à l'écran.

Remarques :

1 - On rappelle également que si on cherche à lire l'attribut class, on doit utiliser depuis javascript "className" car class est un mot-clé de javascript.

2 - Attention également, si vous cherchez à lire le contenu de l'attribut style, il est possible que vous tombiez sur des noms comme background-color" en CSS qui vont devenir en javascript backgroundColor.

var mabalise = document.querySelector("#special");

alert(mabalise.style.backgroundColor);

3 - Attention également à une erreur courante : les valeurs lues via l'attribut STYLE ne correspondent pas aux valeurs contenues dans le fichier CSS : si le background est défini via un fichier CSS externe, vous n'allez pas réussir à lire la couleur actuelle. Dans ce cas, il faudra soit aller créer cette couleur depuis le javascript, soit aller voir la partie qui traite de la liaison entre javascript et CSS.

1.5 Modification ou affectation des attributs

Exactement les mêmes remarques qu'avec les deux propositions pour la lecture :

On peut parfois utiliser les propriétés directement :

Exemple :

var mabalise = document.querySelector("#special");

mabalise.id="nouveau";

alert(mabalise.id);

Ici, on va donc modifier le id en utilisant la propriété js id (qui existe bien) puis afficher la nouvelle valeur.

Mais nous aurions également pu faire autrement en utilisant la méthode setAttribute :

var mabalise = document.querySelector("#special");

mabalise.setAttribute("id", "nouveau") ;

alert(mabalise.id);

1.6 Lecture des noeuds de contenu textuel : propriété InnerHTML

Pour l'instant, nous n'avons fait que lire ou modifier les attributs des balises. Mais, nous pouvons également modifier et lire le contenu textuel : ce qu'on trouve entre la balise d'ouverture et la balise de fermeture :

    <p id ="special">Contenu <strong>suivant</strong> de la section.</p>

Ici, le contenu textuel de la balise p est : Contenu <strong>suivant</strong> de la section.

Pour lire ce contenu dans une balise, il faut utiliser la propriété innerHTML depuis javascript :

<!DOCTYPE html>

<html>

    <p id ="special">Contenu <strong>suivant</strong> de la section.</p>

    <script>

        var mabalise = document.querySelector("#special");

        alert(mabalise.innerHTML);

    </script>

</html>

En testant, on bien bien la totalité de ce qui est compris entre la balise ouvrante et la balise fermante. On a donc la totalité, balise strong comprise.

On obtient Contenu <strong>suivant</strong> de la section.

1.7 Ecriture de contenu textuel : propriété INNERHTML

Il suffit de trouver le noeud de la balise voulue. Et d'utiliser la propriété innerHTML :

<!DOCTYPE html>

<html>

    <p id ="special">Contenu <strong>suivant</strong> de la section.</p>

    <script>

        var mabalise = document.querySelector("#special");

        mabalise.innerHTML = "Changement changement !";

    </script>

</html>

Le texte affiché sur le paragraphe de votre page devrait avoir changé.

2 - Le terrible secret de l'objet Document

Voici un exemple très court de code HTML :

<!DOCTYPE html>

<html>


    <head>

        <meta charset="UTF-8" />

        <title>LE TEXTE DE L'ONGLET</title>

        <meta http-equiv="content-Language" content="fr" />

    </head>


    <body>

        <h3>TITRE AFFICHE SUR LA PAGE</h3>

    </body>


</html>

Nous allons maintenant analyser les balises contenues les unes dans les autres.

La balise principale est la balise HTML : elle contient toutes les autres balises. Elle contient donc les balises HEAD et BODY :

<!DOCTYPE html>

<html>

    <head>

        ...

    </head>

    <body>

        ...

    </body>

</html>

On dira que la balise html possèdent comme enfants (child / children en anglais) les balises head et body.

On dira que les balises head et body ont une balise parent (parent en anglais aussi) : la balise html.

html head body

01° Quelles sont les enfants de la balise HEAD ? Les enfants de la balise BODY ?

...CORRECTION...

La balise HEAD a comme enfants : META, TITLE, META.

La balise BODY a comme enfant H3.

Nous verrons (dans l'activité suivante) que c'est un peu plus complexe que cela : les sauts à la ligne et les espaces peuvent compter également.

Voyons maintenant comment trouver la balise html depuis le CSS.

Nous allons pour cela rajouter directement du javascript dans la page, cela vous évitera de créer trop de fichiers uniquement pour tester les résultats de cette activité.

Nous allons afficher :

02° Utiliser le code ci-dessous pour voir deux méthodes permettant d'accéder au noeud de la balise HTML. Laquelle est la plus rapide ? Document fait-il référence directement à HTML ?

<!DOCTYPE html>

<html>


    <head>

        <meta charset="UTF-8" />

        <title>LE TEXTE DE L'ONGLET</title>

        <meta http-equiv="content-Language" content="fr" />

    </head>


    <body>

        <h3>TITRE AFFICHE SUR LA PAGE</h3>


        <script>


            var mabalise = document.getElementsByTagName("html");

            alert(

                mabalise[0] + '\n' +

                mabalise[0].nodeName + '\n' +

                mabalise[0].nodeType + '\n' +

                mabalise[0].parentNode.nodeName

            );


            mabalise = document.querySelector("html");

            alert(

                mabalise + '\n'+

                mabalise.nodeName + '\n' +

                mabalise.nodeType + '\n' +

                mabalise.parentNode.nodeName

            );


            alert(

                document + '\n' +

                document.nodeName + '\n' +

                document.nodeType

            );

        </script>


    </body>


</html>

...CORRECTION...

On peut accéder au noeud de la balise HTML en utilisant :

* son nom de balise avec getElementsByTagName('html')

* son nom de balise avec querySelector('html') (méthode la plus rapide car sans collection). La méthode querySelectorAll aurait également fonctionné mais nous savons qu'il n'y a qu'une seule balise html par page.


Dans les deux cas, l'objet document (d'identifiant 9) est bien le noeud PARENT de la balise HTML.

On voit donc que document est bien le noeud principal dans votre structure de page. Voici la structure de votre DOM, le Document Object Model :

#document html head body meta title meta h3 script

L'autre mot-clé que nous avons déjà-vu est window.

Si document fait référence au parent de la balise html, l'objet window fait référence à la fenêtre de votre navigateur.

3 - Création de balises

Pourquoi avoir fait cette longue introduction sur la recherche de balises ? Tout simplement car nous allons avoir besoin de trouver la balise-conteneur ('PARENT') afin d'y insérer ses balises-enfants ('CHILD').

Nous avons en réalité déjà réalisé ce type de choses lors de l'activité sur le rajout d'images.

Nous allons devoir suivre la même procédure :

Nous avons déjà créé des images, nous allons créé ici de simpes balises div. Ca peut toujours servir.

03° Créer les fichiers HTML et Javascript à partir des codes suivants. Cliquer sur le bouton pour vérifier qu'il crée affiche bien une alerte.

<!DOCTYPE html>

<html>


    <head>

        <meta charset="UTF-8" />

        <title>Partie III</title>

        <meta http-equiv="content-Language" content="fr" />

        <script src="js_chapitre12.js"></script>

    </head>


    <body>

        <header>

            <h3>CREONS DES BALISES A LA VOLEE</h3>

        </header>


        <section>

            <h1>TITRE DE LA SECTION</h1>

            <input type="button" onclick="rajouter_div()" value="Une DIV, une !">

        </section>


        <footer>

            <p>Ici, c'est le footer.</p>

        </footer>


    </body>


</html>

Pour le js nommé js_chapitre12.js :

function rajouter_div() {

    alert("Test du bouton ok");

}


function demarrage() {

    alert("Détection du js ok");

}


window.addEventListener("load",demarrage);

Passons aux choses sérieuses.

04° Modifier la fonction rajouter_div pour qu'elle crée dans la page une nouvelle div dont on donnera quelques attributs (taille et couleur de fond via un style).

function rajouter_div(){

    var noeudBalise = document.createElement('div');

    noeudBalise.style.backgroundColor = "blue";

    noeudBalise.style.width = "100px";

    noeudBalise.style.height = "100px";

    alert(noeudBalise + '\n' + noeudBalise.nodeName + '\n' + noeudBalise.nodeType);

    alert(noeudBalise.parentNode.nodeName);

}

05° Le bouton permet-il d'afficher les caractéristiques d'une div ? La div s'affiche-t-elle à l'écran ? Affiche-t-on qui est son PARENT (son conteneur) ?

...CORRECTION...

On peut constater que la div est bien créée puisqu'on parvient à afficher son type et son numéro de type.

Par contre, on ne parvient pas l'afficher : elle est dans le document mais elle n'est l'enfant d'aucune balise déjà présente.

D'ailleurs, si vous ouvrez la console Web, vous pourrez constater que cela déclenche une erreur lors de la tentative d'affiche de son noeud-parent (parentNode).

Pour la faire apparaitre et la rattacher au DOM, il va falloir rajouter une dernière ligne et utiliser la méthode appendChild sur le noeud de la balise dans laquelle on veut créé la nouvelle div.

Par exemple :

document.appendChild(noeudBalise);

06° A quoi rattache-t-on la balise div ? Où devrait-elle apparaitre ? Tester pour vérifier votre réponse.

...CORRECTION...

On utilise document.

La nouvelle div devrait donc devenir une balise-enfant du document et se retrouver derrière HTML.

Elle n'apparait toujours pas.

Si vous ouvrez la console Web, vous pourrez constater que cela déclenche même une erreur : vous n'avez pas le droit de rattacher DIRECTEMENT des balises au document.

Comme on ne peut pas rattacher les balises directement dans le document, tentons de rattacher notre div à la balise HTML :

function rajouter_div(){

    var noeudBalise = document.createElement('div');

    var noeudParent = document.querySelector("html");

    noeudBalise.style.backgroundColor = "blue";

    noeudBalise.style.width = "100px";

    noeudBalise.style.height = "100px";

    noeudParent.appendChild(noeudBalise);

    alert("Le noeud parent est : " + noeudBalise.parentNode.nodeName);

}

07° Modifier le code pour parvenir à rajouter les div dans la balise section.

...CORRECTION...

C'est très compliqué ...

Il faut remplacer l'argument "html" par "section" pour la méthode querySelector sur la ligne deux de la fonction.

Le noeudParent va alors pointer vers le noeud de la balise section.

Lorsqu'on fait le raccord avec la méthode appendChild sur noeudParent, tout est donc fait naturellement.

4 - Destruction des balises

Maintenant que nous savons créer les balises, il va être temps d'apprendre à les détruire !

08° Chercher sur Internet de la documentation sur la méthode removeChild.

09° Modifier votre code HTML pour rajouter un autre bouton qui déclenchera une fonction destruction.

<!DOCTYPE html>

<html>


    <head>

        <meta charset="UTF-8" />

        <title>Partie III</title>

        <meta http-equiv="content-Language" content="fr" />

        <script src="js_chapitre12.js"></script>

    </head>


    <body>

        <header>

            <h3>CREONS DES BALISES A LA VOLEE</h3>

        </header>


        <section>

            <h1>TITRE DE LA SECTION</h1>

            <input type="button" onclick="rajouter_div()" value="Une DIV, une !">

            <input type="button" onclick="destruction()" value="Boum !">

        </section>


        <footer>

            <p>Ici, c'est le footer.</p>

        </footer>


    </body>


</html>

10° Créer cette nouvelle fonction destruction. Elle devra :

Souvenez-vous des boucles pour lire les collections d'objets :

    for (var i = 0; i < listeDiv.length; i++) {

La correction possible est un peu plus bas.

Mais comme à chaque fois, il vaut mieux d'abord tenter de trouver la réponse (ou un début de réponse) soi-même.

function destruction(){

    var listeNoeuds = document.querySelectorAll('section div');

    for (var i = 0; i < listeNoeuds.length; i++) {

        var noeud = listeNoeuds[i];

        var noeudParent = noeud.parentNode;

        noeudParent.removeChild(noeud);

    }

}

Dernière remarque :

On peut stocker le résultat de la méthode removeChild dans une variable : on garde alors en mémoire le noeud à nouveau libre.

Et savez-vous ce qu'on peut en faire de ce noeud ? On peut le réaffecter à un autre parent avec la méthode appendChild !

11° Modifier une dernière fois votre fonction destruction. On devra cette fois détruire la balise mais également la transferer dans la balise FOOTER.

La solution finale est ci-dessous :

...CORRECTION...

function destruction() {

    var listeNoeuds = document.querySelectorAll('section div');

    var noeudFooter = document.querySelector('footer');

    for (var i = 0; i < listeNoeuds.length; i++) {

        var noeud = listeNoeuds[i];

        var noeudParent = noeud.parentNode;

        var noeudCopie= noeudParent.removeChild(noeud);

        noeudFooter.appendChild(noeudCopie);

    }

}

Cela devrait vous ouvrir pas mal de portes dans vos projets.

Dans la prochaine activité, nous allons voir comment naviguer encore plus finement dans le DOM. Cela nous permettra ainsi de rajouter les éléments exactement où on le désire.