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
-
Avant de coder, il faut réfléchir. Prendre un feuille de papier et y
noter toutes les étapes de manière grossière, par exemple :
- créer un tableau de ...
- parcourir le tableau avec une boucle for
- pour chaque élément du tableau, récupérer une valeur
- vérifier que cette valeur est comprise entre ... et ... via un if
- si oui, mettre cette valeur dans un tableau
- donc créer un second tableau avant la boucle
- si non, effacer le champ contenant cette valeur
- ...
- Commencez à coder, sans oublier de bien indenter votre code (mettre des espaces au début de certaines lignes) et de le rendre lisible, aéré (mettre des lignes vides entre des lignes de code). Bien écrire vous permettra d'éviter des erreurs et donc vous fera gagner du temps.
- Ne croyez pas que vous allez coder sans faire d'erreur dans le nom d'une fonction,
sans oublier un point-virgule, une accolade, ... placez des
alert("avant le for/if/...");
et desalert("après le for/if/...");
, desalert("variable x = " + x);
ou desalert("jusqu'ici tout va bien");
. Puis, commentez ces instructions -// alert("...");
- au fur et à mesure de vos tests. - Testez et re-testez votre code. 10 fois, 100 fois si nécessaire. Le code, ce n'est pas de la littérature ! Aucune erreur de syntaxe ne sera tolérée. Et, lorsque vous aurez éliminé toutes les erreurs de syntaxe, il vous restera à éliminer les erreurs de logique (les bugs !)
- Pour que votre code ne contienne pas de bugs, il faut tester votre formulaire avec
les données les plus improbables. Si vous demandez un nombre compris entre 1 et 10 et que
le client répond :
bon"jou'r !
votre programme se comporte-t-il comme prévu ? Et, avec des nombres négatifs ? Et, avec des guillemets ? ... Toutes les erreurs possibles sont-elles signalées au client ? Si oui, alors vous pouvez effacer ces alert(). - Placez un maximum de contraintes HTML :
type="number" min="..." max="..." maxlength="..."
- N'oubliez pas que toute réponse du client est une chaîne de
caractères. Lorsqu'il tape
10
la réponse captée sera"10"
... - Écrivez votre code JavaScript en tenant compte du fait que certaines contraintes HTML ne seront appliquées que juste avant l'envoi des données du formulaire.
-
Ne croyez pas que votre code est parfait. S'il fonctionne toujours correctement,
il est juste bon. Essayez de l'améliorer et
/* n'oubliez pas de le commenter */
- Le JavaScript est si puissant qu'il peut reconstruire totalement une page web, mais n'oubliez pas que les codes HTML, CSS et JavaScript travaillent ensemble. Le HTML sert au cadre général, le CSS à la beauté et le JavaScript au calcul. N'utilisez pas le JavaScript pour améliorer la présentation qui apparaît après le chargement de la page. Mais, vous pouvez/devez utiliser le JavaScript pour modifier la présentation si la réponse est bonne. Vous pouvez/devez utiliser le JavaScript pour alléger le fichier HTML lorsque certaines parties du code HTML sont répétitives.
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.