Mini-cours de JavaScript

Jour 10

Dernière journée ! Elle sera courte et instructive. Vous allez apprendre à récupérer une commande de produits, grâce à une méthode et des propriétés relatives à la navigation parmi les balises HTML.

Vous allez également apprendre à alléger un fichier HTML et ainsi réduire l'encombrement sur les autoroutes de l'information.

document.querySelectorAll()

.querySelectorAll() sur MDN

La méthode document.querySelectorAll() de l'objet document permet de sélectionner un ensemble de balises.

La méthode retourne une NodeList (et non un tableau d'éléments ...).
Les boucles for (var item of list) {...} boucleront correctement sur les objets NodeList

Si vous souhaitez créer un formulaire pour vendre vos produits, vous devez créer un champ pour que l'utilisateur puisse indiquer la quantité souhaitée pour chaque produit.

Mais, via document.getElementById(), il vous faudrait créer une ligne de code par champ, puis vérifier avec un if pour savoir si elle est supérieure à zéro et ainsi savoir si votre produit fait partie de la commande. Le cauchemar ... sans parler des erreurs ... Retenez que plus votre code est long, plus le risque d'erreur est grand et plus cela sera difficile de modifier le code.

Heureusement, vous pouvez récupérer TOUS les elements (balises) d'une zone et les placer dans une collection (un quasi tableau).

var list = document.querySelectorAll("#mesProduits input");

Par rapport à var collection = document.getElementById("monChamp"); vous constatez que le code n'est pas compliqué. Remplir un tiroir (une variable) ou remplir une sous-armoire (un tableau) se fait en une ligne de code ! Récupérer un champ ou mille champs n'est pas plus compliqué ! Voilà, une méthode qui facilite grandement la vie des programmeurs.

var list = ne mérite pas d'explication particulière, vous savez créer une variable grâce au mot-clé var.

"#mesProduits input" mérite une explication. Vous avez remarqué que ce qui est mis entre les parenthèses de cette méthode est une chaîne de caractères.

Notez qu'entre les parenthèses de la méthode document.getElementById() on place aussi une chaîne de caractères (qui correspond à la valeur de l'attribut id de la balise visée), appelée : un identificateur.

Une chaîne de caractères qui permet de sélectionner une balise ou un ensemble de balises s'appelle : un sélecteur

En CSS, vous avez appris qu'il existait des sélecteurs (= ce qui se trouve devant les accolades qui contiennent les instructions). En CSS, le sélecteur permet au navigateur de sélectionner un ensemble de balises auxquelles il appliquera l'apparence voulue.

Vous l'avez deviné ! Le paramètre de la méthode .querySelectorAll() est un sélecteur CSS (mis entre guillemets)

En CSS, pour sélectionner une balise particulière on place un dièse (#) suivi (sans espace) de la valeur de l'attribut id de la balise visée.

Rappel. En CSS, le code suivant :

#mesProduits {color:orange}
signifie que le contenu textuel de la balise ayant comme attribut id="mesProduits" sera en orange.

Rappel : Lors de l'étude du HTML vous avez appris qu'une zone peut contenir des zones ... Donc, la couleur de tous les textes de toutes les balises contenues dans la balise <div id="mesProduits"> ... </div> sera en orange.

La première lettre de CSS est C, pour cascade ...

Rappel : La balise <div> est la balise dédiée à la création d'une zone particulière.

Par exemple, pour pouvoir sélectionner facilement tous les champs voulus de votre formulaire, il suffit de créer une zone qui contiendra tous ces champs.

    ...
    <div id="mesProduits">
        <p>Produit 1
            <input name="produit1" type="number" min="1" maxlength="2" size="2">
        </p>
        ...
        <p>Produit 999
            <input name="produit999" type="number" min="1" maxlength="2" size="2">
        </p>
    </div>
    ...

Ensuite, pour sélectionner tous les champs de cette zone, il suffit alors d'écrire comme sélecteur : "#mesProduits input"

var list = document.querySelectorAll("#mesProduits input");

Moralité : Si votre code HTML est bien fait, votre code JavaScript sera court.

Avant de coder, réfléchir !

Un item est un élément d'une collection.
Un "élément" est un objet JS qui représente une balise.

La variable "list" contient une collection de nœuds (et non un tableau d'éléments).
Et, que représente chaque item de cette collection ? Une balise.
Et, plus précisément ? Une balise <input />
Toutes les balises <input /> du formulaire ?
Non ! Uniquement les <input type="number"/>.
Et comment avez-vous fait cela ?
La zone délimitée par les balises <div id="#mesProduits"> et </div> ne contient pas les autres <input /> (de type submit, reset)

Bien. La collection contient donc uniquement les champs à analyser.
Que doit-on vérifier pour chaque champ ?
Pour chaque champ, il faut vérifier que la valeur indiquée par le client soit supérieure à zéro.
Et, comment faire pour vérifier chaque champ ?
Les champs étant dans une collection, il faut le parcourir, item par item.
Et, comment parcourir une collection, élément par élément ?
Il faut utiliser une boucle for ... of.

for (var item of list) {...}

Quel est le nom de la propriété de l'objet - qui représente cette balise <input /> - contenant la valeur tapée ?
value !
Si x est le nom de la variable qui doit contenir la valeur tapée, comment affecter cette variable pour le premier champ ?

x=item.value;

En effet, item représente un objet de la collection (qui représente une balise <input />).
Cet objet qui est une représentation d'une balise <input /> dispose de la propriété value qui représente la valeur de l'attribut value="..." de cette balise
Pour appliquer une propriété à un objet, il suffit de mettre le point entre le nom de l'objet et la propriété.
item.value représente donc la valeur tapée par le client.
Le signe d'affectation est le signe =
Pour affecter la variable x par la valeur tapée, il faut écrire :
x=item.value; (sans oublier le point-virgule)

Que peut valoir cette variable ?
Une chaîne de caractères, car toute valeur d'un champ est une chaîne de caractères.

Puisque la valeur d'un champ est toujours une chaîne de caractères, pourquoi ce champ ne pourrait-elle pas contenir le mot "deux" ?
Bien que le champ soit de type number. Voir le code HTML : <input type="number" />, le client peut taper une chaîne de caractères, car la validation n'a lieu que juste avant l'envoi des données. Donc, le code JavaScript devra s'assurer que ce qui est tapé est un nombre via les fonctions parseInt() ou parseFloat(). Comme la quantité commandé est un nombre entier, on optera pour parseInt().

Et si le client avait tapé "2" dans le premier champ quel est le nombre contenu dans la variable x ?
La variable ne contient pas de nombre ! Mais, elle contiendra la chaîne de caractères : "2"

Et si le client n'avait rien tapé dans le premier champ quel serait le contenu de la variable x ?
La variable x contiendra une chaîne de caractères vide : ""

À quel nombre correspond une chaîne de caractères vide ? à 0 ?
Une chaîne de caractères vide ne représente aucun nombre.

Sachant qu'il ne faut tenir compte que des champs dont la valeur doit représenter un nombre compris entre 1 et 99, quel code commenté proposez-vous ?

/* Mettre tous les objets représentant les "inputs" voulus dans un tableau */
var list = document.querySelectorAll("#mesProduits input");

for(var item of list){

    x=item.value; /* x = une chaîne de caractères*/

    if(x.length>0 && x.length<3 && !isNaN(x)){
        /* Si le programme passe ici => x représente un nombre
        / cependant ce nombre peut être décimal, négatif, valant 0 */

        x=parseInt(x); /* x est devenu un nombre entier */

        if(x>=1) {
            /* Si le programme passe ici => x représente un nombre
            entier supérieur ou égal à 1
            de deux chiffres maximum puisque x.length<3 */

            item.value=x; /* corrige la valeur affichée */

        }else{
            item.value=""; /* car la valeur de ce champ n'était pas valide */
        }
    }
}

item.value= permet de modifier l'affichage de la page. Si ce que le client a tapé est presque bon ("3." = un "3" suivi d'un point décimal), sera affiché l'entier positif utilisé ("3"). Sinon ce qui a été tapé est remplacé par une chaîne de caractères vide.

Vous savez qu'il est possible de résoudre un problème de différentes manières. On pourrait aussi s'assurer que la chaîne correspond à un nombre entier via une expression régulière. Mais l'étude des expressions régulières dépasse le cadre de ce mini-cours.

Maintenant, vous êtes capable d'identifier tous les champs contenant une quantité valide. Mais, vous n'êtes pas capable de dire à quel produit correspond cette quantité.

Apprenons à naviguer parmi les nœuds ...

Maintenant, nous pouvons récupérer les quantités supérieures à 1 ainsi que le nom du produit concerné.

Cependant, le client peut choisir plusieurs produits. Il nous faut donc stocker la liste des produits choisis.

Dans quelle variable peut-on stocker plusieurs données ? Une variable-tableau

Comment crée-t-on une variable-tableau ?

var monTableau = new Array();

De combien de tableaux avons-nous besoin ?
Deux. Un pour les quantités et un pour les noms des produits concernés.

On pourrait aussi utiliser un seul tableau (un tableau de tableaux) ou un tableau d'objets. Mais, restons simple.

Complétons notre code

/* Mettre tous les objets représentant les "inputs" voulus dans un tableau */
var tableau = document.querySelectorAll("#mesProduits input");

var noms = new Array(); /* tableau contenant les noms de produits sélectionnés */
var quantites = new Array();  /* tableau contenant les quantités */

for(i=0,taille=tableau.length,x;i<taille;i++){

    x=tableau[i].value; /* x = une chaîne de caractères */

    if(x.length>0 && x.length<3 && !isNaN(x)){
        /* Si le programme passe ici => x représente un nombre
        cependant ce nombre peut être décimal, négatif, valant 0 */

        x=parseInt(x); /* z est devenu un nombre entier */

        if(x>=1) {
            /* Si le programme passe ici => x représente un nombre
            entier supérieur ou égal à 1
            de deux chiffres maximum puisque x.length<3 */

            tableau[i].value=x; /* corrige la valeur affichée */

            noms.push(tableau[i].parentNode.firstChild.nodeValue);
            quantites.push(x);

        }else{
            tableau[i].value=""; /* car la valeur affichée n'était pas valide */
        }
    }
} /* À ce stade, les tableaux "noms" et "quantites" sont remplis */

Il ne nous reste plus qu'à afficher un résumé de la commande, avant que le client ne valide sa commande.

Comment écrire du texte qui n'existait pas lors de la création du fichier HTML ?
Grâce à la propriété .innerHTML

Sur quel objet va s'appliquer cette propriété .innerHTML ?
Sur un objet représentant une balise.

Cette balise sera-t-elle quelconque ?
Non, cette balise n'aura pas de contenu, mais sera identifiée via son attribut id.

Cette balise sera donc la balise <output> dédiée à l'affichage de résultats.
Non ! Car la balise <output> est de type inline.
Comme le client peut choisir plusieurs produits et que, chaque produit choisi, sera repris sur une ligne, il est préférable de choisir une balise de type block.

<p id="apercu"></p>

Mais, vous avez fait une faute ! On écrit "aperçu" et non "apercu" !
Non. La valeur de l'attribut de l'id est un identifiant.
Pour éviter tout risque, j'applique pour les identifiants (et pour nom de fonctions) les règles de nommage des variables du C qui interdisent les caractères accentués.

Quel code proposez-vous pour construire le contenu de la variable, msg, qui sera utilisé pour afficher la commande ?

  var msg="";
  if(noms.length>0){
    msg="Votre commande :<br>";
    for(i=0,n=noms.length;i<n;i++){
      msg+="<br>"+quantites[i]+" "+noms[i];
    }
  }
  document.getElementById("apercu").innerHTML=msg;

Il ne reste donc plus qu'à créer un bouton pour afficher ce résumé, un pour l'effacer.

Vous pouvez regarder le code-source de exercice11.htm et celui du fichier .js appelé par le fichier HTML.

Si le code de votre fonction ne fonctionne pas, tapez alert(""); dans votre code. Si une fenêtre s'affiche, le programme est passé jusque là. Une fois passé, commentez l'instruction. Regardez le code source de ce fichier.

Événement : load

Le clic est un événement, la frappe d'une touche est un événement, la fin du chargement du fichier HTML dans la mémoire est aussi un événement qui s'appelle : load. Cet événement se produit avant l'affichage de la page web.

L'attribut HTML onload est quasiment utilisé qu'avec la balise <body>.

La valeur de cet attribut est souvent le nom d'une fonction qui sera exécutée à la fin du chargement du fichier HTML dans la mémoire.

Cette fonction est souvent appelée pour terminer la présentation de la page ou incrémenter un compteur de visites.

.setAttribute()

La méthode .setAttribute() appliquée à un objet element (une balise) permet de mettre à jour les attributs HTML de cette balise. Le premier paramètre est le nom de l'attribut; le second, la valeur de cet attribut.

Dans notre exemple, on voit, dans le code source du fichier HTML, des <input ... type="number" min="1" maxlength="2" size="2"> on constate donc que plusieurs attributs de balises sont les mêmes. Ceci alourdit le fichier HTML, rend le code HTML moins lisible et encombre inutilement les autoroutes de l'information.

Puisque nous savons que ce qui est affiché à l'écran est une représentation de la mémoire, que le JavaScript permet de modifier cette mémoire, que la méthode .querySelectorAll() permet de sélectionner un ensemble de balises et que la méthode .setAttribute() permet de mettre à jour les attributs d'une balise, il nous reste pour qu'à écrire :

function completerPageWeb(){ /* Ajoute les attributs */
    var x = document.querySelectorAll("#produits input");
    for(i=0,taille=x.length;i<taille;i++){
        x[i].setAttribute("type","number");
        x[i].setAttribute("maxlength","2");
        x[i].setAttribute("min","1");
    }
}

La taille des champs size="2" relève de l'apparence. Le langage le plus approprié est donc le CSS. Et, comme la mise en forme d'un formulaire ne concerne pas toutes les pages du site internet, le code CSS sera écrit dans le fichier HTML dans la zone dédiée au style (elle-même comprise dans la zone <head>).

<style>#produits input {width:40px;}</style>

On constate que le sélecteur CSS est identique que celui de la méthode .querySelectorAll() utilisée en JavaScript.

Toutefois, pour éviter que votre éditeur de code HTML ne "corrige" votre code CSS - comme votre code JavaScript - il est recommandé de placer le code non-HTML dans un fichier externe. Même si ce fichier ne contient qu'une seule ligne de code.

Pour que les instructions de la fonction completerPageWeb() soient exécutées, il faut appeler cette fonction. Dans l'exemple, la mise à jour des balises <input /> doit avoir lieu juste après la fin du chargement du fichier HTML et avant l'affichage de la page web. Il suffit donc d'écrire :

<body onload="completerPageWeb();">

Toutefois, il est plus élégant de placer l'appel de la fonction dans le script lui-même.

Le client ne verra aucune différence. La mémoire est la même, la page affichée est la même. La seule différence est un code HTML plus lisible, un fichier HTML plus petit, une rapidité d'affichage plus grande et probablement un meilleur référencement de la page dans les moteurs de recherche.

onsubmit="return envoyer();"

Vous pouvez regarder le code-source de exercice12.htm et celui du fichier .js appelé par ce fichier HTML. Vous constaterez que le code HTML est plus léger, que le montant total de la commande est affiché, qu'un clic dans un champ efface l'aperçu et que les deux boutons relatifs à l'aperçu sont à l'intérieur du formulaire ainsi que le bouton de type="reset".

On constate dans le code HTML <form onsubmit="return false;" .... La valeur de l'attribut n'est pas une fonction. Et on constate que, sans cette valeur - return false; -, sans avoir modifié le code JavaScript le contenu du panier ne s'affiche plus. Pire, la page se réaffiche après avoir vidé tous les champs.

Le code JavaScript fonctionne avec le code HTML. Un bon code JavaScript peut ne pas fonctionner si le code HTML n'est pas adapté.

Coder ne se résume pas à faire des copier/coller ...

<form onsubmit="return false;" ... permet aux boutons relatifs à l'affichage du panier d'être placés dans le formulaire. Mais, cela annule l'envoi des données ...

Le code source exercice13.htm affiche <form onsubmit="return envoyer();". Si la fonction envoyer() retourne true, les données sont envoyées, si elle retourne false, elles ne seront pas envoyées.

Nous souhaitons que les données ne soient envoyées qu'après que le client ait vu son panier (et le montant total de sa commande). La fonction afficher() affiche le panier, la fonction effacer() efface le panier et la fonction envoyer() doit retourner true ou false. Une solution simple consiste à déclarer une variable de type booléen à l'extérieur de ces fonctions pour qu'elle soit accessible par toutes les fonctions. À la fin de la fonction afficher(), elle sera mise à true pour dire que le client peut envoyer sa commande. À la fin de la fonction effacer(), elle sera mise à false pour dire que le client ne peut pas envoyer sa commande.

Il faut éviter au maximum de déclarer des variables hors des fonctions.
Mais, parfois c'est très utile.

<noscript>

Dans l'exercice 13, les deux boutons créés via le JavaScript n'apparaîtront pas si le client a désactivé le JavaScript. Le formulaire sera alors encore opérationnel, mais les facilités offertes au client - afficher le contenu du panier - ne seront plus disponibles.

Si le Javascript n'est pas activé, il est recommandé de le signaler à l'utilisateur via la balise <noscript>.

La balise <noscript> et son contenu seront idéalement placés juste après la balise <body>.

Code HTML :

  <body>
    <noscript><strong>Le JavaScript n'est pas activé</strong>.<br />
     <br />
    Son activation est nécessaire pour bénéficier de toutes les fonctionnalités.<br />
    <a href="https://www.enable-javascript.com" target="_blank">Comment activer JavaScript ?</a>
    </noscript>
  ...

Il est possible d'ajouter une belle apparence au contenu de cette balise via un peu de CSS.

Code CSS :

noscript{
  display: block; /* pour permettre le centrage */
  text-align: center;
  padding: 10px;
  background-color: #57FFFF;
  color: #000000;
}

La balise <noscript> ne devrait pas être entourées de balises, car l'espace et le style de ces balises seront utilisés même si le contenu de la balise <noscript> n'est pas affiché.
<p style="text-align: center"><noscript>...</noscript></p> est donc un mauvais code. Ceci explique pourquoi display: block;

Il est aussi possible de cacher certaines zones (de type block) qui sont inutiles en l'absence de JS via une classe CSS.

Code CSS :

.ifNoJS{
  display:none;
}

Et, si le JS est activé, exécuter le code JS qui affiche ce qui était initialement caché.

Code JS :

var tab_NoJS = document.getElementsByClassName("ifNoJS"); var i_NoJS;
for (i_NoJS = 0; i_NoJS < tab_NoJS.length; i_NoJS++) { tab_NoJS[i_NoJS].style.display = "block"; }

Ce code devrait être au début du fichier JS (lié au fichier HTML). On constate que les variables sont suffixée par _NoJS. Ceci pour éviter que les mélanger avec les autres variables du fichier.

La création d'éléments - comme des boutons dans exercice13.js - dépasse le cadre de ce mini-cours. Mais, rien ne vous interdit d'aller plus loin.

Vous avez maintenant de bonnes bases pour coder, mais ...

Avant de coder, il faut réfléchir

Encore un dernier mot ...

Conclusion

Maintenant, vous savez que la page web que vous voyez à l'écran est une image de ce qui est dans la mémoire du navigateur et que via le code JavaScript vous permet de modifier cette mémoire.

Le JavaScript est si important que je vous recommande de placer juste avant la fin du body : <script src="x.js"></script> sur toutes vos pages d'un même dossier. Même si ce fichier n'existe pas. En effet, si vous souhaitez dans quelques mois modifier l'apparence de toutes vos pages de ce dossier, il vous suffira d'éditer ce seul fichier.

Grâce à cette initiation au langage JavaScript, vous savez qu'un programme est une suite d'instructions regroupées dans une fonction qui peut appeler d'autres fonctions, qu'il fait appel à la mémoire de l'ordinateur, qu'il est "intelligent" lorsqu'il peut faire des choix en fonction de ce qui est contenu en mémoire et qu'il est "puissant" car il peut répéter des instructions.

Grâce à cette initiation à la programmation orientée objet, vous savez que ce qui semble simple - afficher une boîte de dialogue - est, en réalité, très complexe; et, que pour faciliter la vie du programmeur la plupart des langages de programmation utilise des objets : C++, Java, JavaScript (pas le C). Il suffit alors d'utiliser les méthodes et propriétés des objets dans nos fonctions pour faire facilement tout ce qu'on veut.

Via ce mini-cours, vous n'avez eu qu'un aperçu de la puissance du JavaScript.