Infoforall

Javascript 08 : GERER L'INTERFACE

Nous allons voir comment définir dans le code javascript certaines caractéristiques qu'on définissait jusqu'à présent dans le CSS ou dans le HTML directement.

L'intérêt de faire cela est qu'on pourra alors changer les choses automatiquement. Nous allons ainsi réaliser (sur 3 activités) un petit 'jeu' nous permettant de modifier la couleur des cases d'une interface à chaque fois qu'on appuie sur l'une des div. On pourrait également modifier la taille des images par exemple. Ou changer au hasard la couleur d'un carré lorsqu'on clique dessus.

Cette activité est la suite logique de l'activité "Interface" de la partie CSS. Commencez donc par cela puis revenez ici ensuite :

1 - Création de l'interface

Nous allons commencer par créer une interface basique et nous allons la modifier via un script au lancement de la page.

Imaginons que nous voulions obtenir ceci : (un carreau correspondant à 50 pixels) un affichage de 800 pixels sur 650 pixels.

interface voulue

Ainsi le carré bleu commence en (x=50;y=50). Le carré orange commence en (x=300;y=50)...

Par contre, contrairement à ce que nous avons fait avec l'activité CSS, nous voudrions que la couleur d'une div change lorsqu'on clique dessus.

Voici la partie HTML :

<!DOCTYPE html>

<html>

    <head>

        <meta charset="UTF-8" />

        <title>INTERFACE GRAPHIQUE</title>

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

        <link rel="stylesheet" href="interface.css" />


        <script type="text/javascript" src="interface.js"></script>


    </head>

    <body>


        <header>

            <h3>MON TEST D'INTERFACE</h3>

        </header>


        <section>


            <div id="js07_test1">

                <div id="a11"></div>

                <div id="a12"></div>

                <div id="a13"></div>


                <div id="b21"></div>

                <div id="b22"></div>

                <div id="b23"></div>


                <div id="c31"></div>

                <div id="c32"></div>

                <div id="c33"></div>

            </div>


        </section>


    </body>

</html>

Remarquez les noms des fichiers CSS et JS. Remarquez également qu'on ne placera plus le fichier js à la fin de la balise body : sur une interface plus conséquente que celles réalisées jusqu'à présent, mettre le script à la fin de la page ne garantira pas nécessairement que tous les éléments sont chargés avant de lancer le script. Nous allons voir une méthode pour être certain de bien charger la page avant de lancer le script.

Et le CSS : (qu'on nommera interface.css)

On commence avec la div-conteneur js07_test1 :

#js07_test1 {

    margin: 0px;

    padding: 0px;

    border: 1px solid #660000;

    width: 800px;

    height: 650px;

}

Je définis une balise div-conteneur en lui donnant la bonne largeur (800px) et hauteur(650px).

On passe maintenant au CSS commun des div de couleurs contenues dans la div-conteneur. Nous créons des carrés de 150px de côté :

#js07_test1 div {

    display: inline-block;

    margin: 0px;

    padding: 0px;

    width: 150px;

    height: 150px;

    border: 1px solid #777777;

}

01° Télécharger le fichier HTML, le fichier CSS, et le fichier Javascript (vide). Placez le tout dans un même dossier.

Nous allons maintenant créer l'interface pas à pas à partir de cette page HTML presque vide.

Attention : il ne faut faire cela qu'avec des pages nécessitant de l'interactivité (comme un jeu) car on peut très bien avoir affaire à un navigateur dans lequel l'utilisateur a désactivé le javascript.

2 - Attendre le chargement d'une page avant de lancer le javascript

Commençons par remplir le fichier Javascript avec le code minimal pour éviter qu'il ne se charge avant que la page ne soit totalement affichée.

02° Rajouter le code suivant dans le fichier javascript :

function demarrage() {

    alert("go !");

}


window.addEventListener("load",demarrage);

Que fait ce code ?

La première partie ne fait rien : on donne juste la définition, le contenu, d'une fonction nommée demarrage.

La dernière ligne crée ce qu'on nomme en anglais un EventListener : une surveillance d'événement :

Comme vous le constatez, cette méthode nécessite l'envoi de deux arguments :

  1. Le premier est un string contenant l'événement à surveiller ("load", "click", "change"...)
  2. Le second est le nom de la fonction à utiliser lorsque l'événement se produit.

Et voilà : maintenant, il vous suffit de placer votre code actif dans la fonction demarrage et vous serez certain de ne pas faire référence à une div HTML qui n'est pas encore apparue dans le code.

3 - Changer l'attribut et le style d'une balise HTML

Nous allons maintenant commencer à modifier les balises div.

Commençons par la première d'id a11.

Nous voudrions que lors du démarrage de la fenêtre :

Nous savons déjà modifier le contenu HTML d'une balise texte ou l'attribut value d'une balise input.

Mais, comment modifier le style d'une balise ?

En réalité, c'est simple et c'est exactement le même principe :

function demarrage(){

    var balise1 = document.getElementById("a11");

    balise1.style.backgroundColor = "blue";

    var balise2 = document.getElementById("a12");

    balise2.style.backgroundColor = "orange";

}

Comme vous le voyez : on va chercher la référence de la balise html d'id a11, on place le résultat dans balise1 et on y cherche le style et dans le style, on cherche la propriété background-color. Avec le =, on lui affecte la valeur "blue".

03° Que va-t-il se passer pour la balise d'id "a12" ?

...CORRECTION ...

On va changer sa couleur de fond et la placer en orange.

04° Qu'est-ce qui est bizarre avec l'instruction permettant de modifier la couleur du background ?

...CORRECTION ...

La propriété CSS se nomme background-color mais on note simplement backgroundColor dans le code JS.

La solution est ici :

Les différences entre les noms des propriétés CSS et les noms des propriétés utilisées depuis un script

Javascript et CSS n'ont pas toujours le même nom de propriété. Presque toujours, sauf dans deux cas :

Nom composé : Si le nom de la propriété est composé (comme background-color), dans javascript il faudra supprimer le - et remplacer la minuscule du second mot par une Majuscule.

Ainsi background-color dans le code CSS devient backgroundColor dans le code javascript !

L'attribut class : on pourrait récupérer la valeur ou changer la valeur contenue dans la class d'une balise HTML. Il n'y aura aucune différence par rapport avec ce qu'on a fait avec value par exemple. Sauf, sauf, sauf que ... class est un mot-clé dans javascript. C'est un mot comme function, true ou false. On ne peut pas l'utiliser comme nom de variables ou autres. Comment faire alors ?

Comme haut dessus en fait : il faut connaitre le langage et sa façon de communiquer.

Ici, on remplacera class par className. Et le tour est joué.

05° Rajouter les lignes de code pour avoir deux autres div à la bonne couleur :

interface voulue

Vous imaginez faire ça avec vos 9 div ? C'est un peu long non ?

Heureusement, il y a mieux.

Est-ce compatible avec le fichier CSS ?

Oui et non.

Premièrement, souvenez-vous (c'est le chapitre web 2, ça date) que les propriétés fournies directement dans le HTML avec le style sont prioritaires sur les propriétés définies dans le fichier CSS.

Conclusion : le javascript va ici écraser totalement le CSS en cas de conflit entre deux valeurs.

Seconde chose : si vous cherchez à lire une propriété CSS vous allez obtenir une réponse vide : le code javascript vu ici lit ici le contenu des attributs style dans la page HTML et pas dans le fichier CSS.

Nous verrons plus tard qu'il est possible d'aller voir dans le CSS mais le plus simple pour l'instant est :

  • De définir dans le CSS les propriétés d'affichage qui n'auront pas à être modifié par le javascript
  • De définir les autres directement en javascript avec un code du style balise2.style.backgroundColor =.

4 - Sélectionner une liste de balises

Pour l'instant, vous savez sélectionner une balise HTML à l'aide de son identifiant. Pour cela, il "suffit" d'utiliser un code du type :

var reference_balise = document.getElementById("identifiant_voulu");

Si on veut rester cohérent avec le langage XML, la reférence devrait plutôt être dénommée "node" en anglais, "noeud" en français.

Nous verrons pourquoi lors de l'activité sur la structure DOM des documents HTML.

Cette commande vous permet d'obtenir le node/noeud d'une balise et une seule.

Et si je n'ai pas placé d'attribut id sur ma balise, hein ?

Et bien, ce n'est pas très compliqué : on peut sélectionner une balise en utilisant le même fonctionnement qu'avec le CSS. Comment ? A l'aide de la méthode querySelector.

Le tout est de se souvenir que la méthode ne va renvoyer que la première balise qui correspond à la spécification donnée. Revoir le chapitre sur le CSS pour savoir comment on précise cette spécificité.

  • par document.querySelector("#mon_titre") (avec un #) si "mon_titre" est un Id dans le HTML.
  • par document.querySelector(".mon_titre") (avec un point) si "mon_titre" est une class définie dans le HTML et que la première balise rencontrée avec cette classe et bien celle qui vous intéresse.
  • par document.querySelector("h3") si la première balise h3 rencontrée est bien la balise voulue.

Comme ça, ça n'a pas l'air, mais c'est très puissant car les sélecteurs CSS peuvent assez facilement cibler une balise bien particulière.

Par contre, ici, cela ne peut pas fonctionner car nous avons plusieurs balises div à gérer. Il va donc falloir utiliser de nouvelles méthodes.

Lesquelles ? Les méthodes avec Elements (avec un s) ou All dans le nom.

Nous allons retrouver ici beaucoup d'éléments que nous avions déjà vu lorsque nous avons rencontré les Arrays, les tableaux.

4-1 La méthode getElementsByTagName

Avec cette méthode, nous allons pouvoir sélectionner toutes les balises h1, ou div ou p ... En anglais, balise peut se traduire par tag.

06° Remplacer votre fonction demarrage par le code suivant :

function demarrage() {

    var listeDiv = document.getElementsByTagName("div");

    alert(listeDiv); // Première alerte

    alert("Nombre d'éléments trouvés dans listeDiv: " + listeDiv.length); // Deuxième alerte

    alert(listeDiv[0]+"\n"+listeDiv[1]); // Troisième alerte

}

07° 1er Alert : Comment se nomme l'objet obtenu en retour de la méthode getElementsByTagName (avec un s) ?

...CORRECTION...

On obtient HTML Collection.

Une collection est un ensemble d'objets, assez proche des Arrays.

08° 2e Alert : Comment obtenir le nombre d'éléments contenus dans une collection ? Combien la méthode a-t-elle trouvée de div ? En regardant le code HTML, dire si la première div trouvée correspond à une div colorée ou à la div-conteneur.

...CORRECTION...

La méthode trouve 10 div.

La première est la div-conteneur des 9 autres div que nous allons vouloir mettre en couleur.

            <div id="js07_test1">

                <div id="a11"></div>

                <div id="a12"></div>

                <div id="a13"></div>


                <div id="b21"></div>

                <div id="b22"></div>

                <div id="b23"></div>


                <div id="c31"></div>

                <div id="c32"></div>

                <div id="c33"></div>

            </div>


09° 3e Alert : Quel est le type des éléments 0 et 1 de la collection ?

...CORRECTION...

Ce sont des div.

C'est noté dans l'alert !

Passons à l'application pratique : mettons nos div en couleurs.

function demarrage() {

    var listeDiv = document.getElementsByTagName("div");

    for (var numIndex = 1; numIndex < listeDiv.length; numIndex++) {

        listeDiv[numIndex].style.backgroundColor = "blue";

    }

}

10° Utiliser ce code : vos balises 'colorées' devraient toutes devenir bleues.

11° Pourquoi ne pas avoir commencé la boucle for avec numIndex = 0 comme d'habitude ?

...CORRECTION...

La boucle génére un compteur variant de 1 à 9 puisqu'on arrête strictement avant d'atteindre la longueur de la collection, à savoir 10.

On a décidé de ne pas commencer en 0 car on ne veut pas de la première div d'indice 0 : il s'agit de la div-conteneur : allez voir le code HTML si vous n'êtes pas convaincu.

Vous pouvez vous amuser à mettre 0 : vous allez constater que l'ensemble du conteneur devient bleu !

12° Pourquoi le code fonctionne-t-il ? Tenter de l'expliquer ligne par ligne au niveau de la boucle.

...CORRECTION...

On commence avec listeDiv[1] qui correspond au noeud/node du premier carré puisqu'il s'agit de la deuxième div qui apparait dans le HTML.

On modifie sa couleur comme vu précédemment, en utilisant style.

On passe ensuite à listeDiv[2] et on fait de même ...

On passe ensuite à listeDiv[3] et on fait de même ...

On peut se passer de sélectionner le div-conteneur dans la collection des références obtenues ?

Oui mais le code deviendra un peu plus compliqué. On pourrait par exemple faire ceci :

function demarrage(){

    var listeDiv = document.getElementsById("js07_test1").getElementsByTagName("div");

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

        listeDiv[numIndex].style.backgroundColor = "blue";

    }

}

On va donc chercher dans le document la balise d'identifiant conteneur (qu'il faudra placer dans le code HTML) :

document.getElementsById("js07_test1")

Puis on sélectionne dans celle-ci toutes les balises div :

document.getElementsById("js07_test1").getElementsByTagName("div");

Du coup, je commence ma boucle à 0 car le premier élément de la collection est bien un des carrés à modifier.

C'est plus compliqué mais nous sommes ainsi certain que cela fonctionne même si on rajoute des balises div au dessus de la balise div-conteneur par la suite.

On pourrait même supprimer les id des carrés si on le voulait : pour l'instant, ils ne servent plus à rien.

4-2 La méthode getElementsByClassName

Avec cette méthode, nous allons pouvoir sélectionner toutes les balises possèdant la classe précisée en argument.

Ca peut être pratique.

Exemple 1 :

document.getElementsByClassName('carre')

Cela vous permet de récupérer la collection des balises ayant une classe 'carre'.

Exemple 2 :

document.getElementsByClassName('carre bleu')

Cela vous permet de récupérer la collection des balises ayant une classe 'carre' ET une classe 'bleu'.

4-3 La méthode querySelectorAll

Avec cette méthode, nous allons pouvoir cibler les balises en utilisant une spécification CSS.

Si vous savez cibler en CSS, vous serez ainsi cibler en javascript.

Ici, nous voulons cibler les balises div contenues dans la balise d'id js07_test1 : #js07_test1 div.

13° Tester le code suivant pour vérifier qu'il fonctionne bien. Comme il ne fonctionne pas tout à fait comme voulu, modifier le code pour que tous les carrés soient rouges.

function demarrage(){

    var listeDiv = document.querySelectorAll("#js07_test1 div");

    for (var numIndex = 1; numIndex < listeDiv.length; numIndex++) {

        listeDiv[numIndex].style.backgroundColor = "red";

    }

}

...CORRECTION...

Vous avez vraiment cherché ? Non ?

Bon : on a laissé var i=1 alors que cette fois l'index 0 correspond bien à la première div à colorer.

En effet, on a la liste/collection des div contenues dans la div-conteneur, pas l'ensemble des div.

C'est bien beau mais toutes les balises sont désormais en rouge. Or, on voudrait changer les couleurs en fonction de la position.

Une solution (longue) consiste à faire des tests if en fonction de la valeur de l'indice i :

function demarrage() {

    var listeDiv = document.querySelectorAll("#js07_test1 div");

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

        if (numIndex==1) {

            listeDiv[numIndex].style.backgroundColor = "green";

        } else {

            listeDiv[numIndex].style.backgroundColor = "blue";

        }

    }

}

14° Changer également la couleur de la troisième div en rajoutant un else if à ce code.

Un peu long si on devait faire cela pour toutes les divs, non ?

5 - Utiliser les tableaux pour automatiser les tâches

Heureusement, on peut automatiser tout cela en utilisant les tableaux de type Array. Qu'est-ce qu'un tableau ? C'est un ensemble ordonné de données et on peut y accéder à l'aide de l'indice de leurs positions.

Le premier élément est numéroté 0.

Le second élément est numéroté 1.

Le plus simple serait donc d'utiliser un tableau de couleurs pour savoir quelle couleur mettre à quelle div :

Case N°Valeur stockée
0'blue'
1'orange'
2'green'
......
8'grey'

En Javascript, cela se codifie de deux façons :

var tCouleurs = [ 'blue', 'orange', 'green', 'blue', 'orange', 'green', 'blue', 'orange', 'green' ];

On notera qu'à la fin, on ne place plus de virgule après le dernier élément.

15° Que contient tCouleurs[2] ?

...CORRECTION...

tCouleurs[0] contient 'blue'.

tCouleurs[1] contient 'orange'.

tCouleurs[2] contient 'green'.

16° Utiliser et modifier le code suivant pour afficher les bonnes couleurs :

var tCouleurs = [ 'blue', 'orange', 'green', 'blue', 'orange', 'green', 'blue', 'orange', 'green' ];


function demarrage() {

    var listeDiv = document.querySelectorAll("#js07_test1 div");

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

        listeDiv[numIndex].style.backgroundColor = tCouleurs[numIndex];

    }

}


window.addEventListener("load",demarrage);

Il est temps de mettre cela en pratique : nous allons placer les divs au bon endroit.

interface voulue

Vous avez réussi à gérer les couleurs, il vous reste à rajouter le style pour le placement des div.

Il faudra donc définir position: relative pour la balise-conteneur. Vous pouvez le faire simplement en CSS puisqu'on ne changera pas cette propriété.

Ensuite, il faudra définir (dans le javascript) des propriétés via l'attribut style permettant :

17° Mini-projet : A vous de jouer. Modifier le code CSS, js, rajouter les tableaux nécessaires mais réaliser l'affichage ci-dessus en définissant les couleurs et positions des div directement depuis le javascript.

Vous trouverez deux solutions possibles ci-dessous mais il en existe beaucoup plus.

Allez les voir une fois votre travail fini. Vous pourrez ainsi voir la deuxième solution puisque vous n'avez dû en utiliser qu'une normalement.

Code CSS commun aux deux solutions : j'ai décidé de placer la propriété position: relative de la balise-conteneur dans le CSS et celles des div colorées dans le javascript. C'est un choix, on peut faire autrement.

#js07_test1 {

    margin: 0px;

    padding: 0px;

    border: 1px solid #660000;

    width: 800px;

    height: 650px;

    position: relative;

}


#js07_test1 div {

    display: inline-block;

    margin: 0px;

    padding: 0px;

    width: 150px;

    height: 150px;

    border: 1px solid #777777;

    position: relative;

}

1er Solution javascript : utilisation des tableaux :

Dans cette solution, j'ai calculé à la main les valeurs des positions absolues top et left et je les ai placé dans deux tableaux nommés tTop et tLeft.

A chaque tour de boucle for, j'affecte aux différents div 'colorées' une propriété position: absolute et des propriétés left et top. En plus de la propriété backgroundColor qui reste bien entendu en place.

J'utilise le préfixe t pour me souvenir que ces variables contiennent des tableaux.

De même, j'utilise une variable i moins explicite que numIndex mais moins longue à taper.

var tCouleurs = [ 'blue', 'orange', 'green', 'red', 'yellow', 'black', 'purple', 'cyan', 'grey' ];

var tTop = [ '50px', '50px', '50px', '250px', '250px', '250px', '450px', '450px', '450px' ];

var tLeft = [ '50px', '300px', '550px', '50px', '300px', '550px', '50px', '300px', '550px' ];


function demarrage(){

    var listeDiv = document.querySelectorAll("#js07_test1 div");

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

        listeDiv[i].style.backgroundColor = tCouleurs[i];

        listeDiv[i].style.position = 'absolute';

        listeDiv[i].style.top = tTop[i];

        listeDiv[i].style.left = tLeft[i];

    }

}


window.addEventListener("load",demarrage);

2e Solution javascript : utilisation de calculs automatisés :

On remarque les div 0,1,2 ont la même propriété top.

Idem pour les div 3,4,5 et les div 6,7,8.

On peut ainsi utiliser le fait que parseInt(i/3) va renvoyer la même valeur pour 0-1-2 (à savoir 0), 3-4-5 (à savoir 1) et 6-7-8 (à savoir 2).

De même, l'observation permet de voit que les propriétés left sont les mêmes pour les balises 0-3-6, 1-4-7 et 2-5-8.

Nous pouvons utiliser l'opérateur % qui renvoie le reste des divisions entières. Ainsi i%3 va renvoyer 0 pour 0-3-6, renvoyer 1 pour 1-4-7 et renvoyer 2 pour 2-5-8.

Du coup, nous n'avons plus besoin de rentrer à la main les valeurs dans les deux tableaux.

var tCouleurs = [ 'blue', 'orange', 'green', 'red', 'yellow', 'black', 'purple', 'cyan', 'grey' ];


function demarrage(){

    var listeDiv = document.querySelectorAll("#js07_test1 div");

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

        listeDiv[i].style.backgroundColor = tCouleurs[i];

        listeDiv[i].style.position = 'absolute';

        listeDiv[i].style.top = (50+200*parseInt(i/3))+'px';

        listeDiv[i].style.left = (parseInt(50+250*(i%3)))+'px';

    }

}


window.addEventListener("load",demarrage);

Le résultat en image ici :

Dans les deux activités suivantes, nous verrons comment :