Dynamisez vos pages Web avec JavaScript

Utiliser conjointement les formulaires HTML et JavaScript

Dans les exemples du chapitre précédent (les bases du JavasScript) l'utilisateur communique avec le programme grâce aux fonctions de saisie de JavaScript :prompt, confirm, alert. C'est pas très ergonomique ...

Si JavaScript est un excellent langage de programmation pour le Web il faut cependant constater que les entrées et sorties sont minimalistes ...

Heureusement en HTML nous avons un outil formidable : le formulaire.
Il faut donc souvent coupler JS et HTML dans la même page pour obtenir une "application JS ergonomique".

Formulaire HTML & JS : premier exemple

Le code CSS et HTML

... <style> label, input, button {display : inline-block ; width : 45% ; height : 40px ; margin : 10px ; vertical-align : top ; } label {text-align : right ; } ... <form name ="f"> <label>votre nom : </label> <input type = 'text' name ="nom"> <label>votre prénom : </label> <input type = 'text' name ="prenom"> <label></label><button type = 'button' name ="bouton">Concaténez ! </button> <label>Nom et prénom : </label> <input type ='text' readonly name ="nom_prenom"> </form> ...

Commentaire du CSS

Dans un formulaire on utilise surtout les balises input, label, button. Ces balises sont par défaut de type inline alors que la balise form est de type block. Or nous voulons avoir la légende (balise label) du champ puis le champ de saisie (balise input) alignés. Il faut donc que ces balises deviennent de type inline-block c'est à dire des boites qui comme toutes les boites peuvent être dimensionnées mais qui se positionnent côté à côté tant qu'il y a de la place.

Ici chaque légende ou champ a une largeur interne de 45% de la largeur de la boite parente (FORM). Donc il y aura deux contrôles par ligne (la balise LABEL et la balise INPUT).

Commentaire du HTLM

Les attributs action et method de la balise form sont ici inutiles puisque ce formulaire n'a pas pour objet d'envoyer les données saisies dans une autre page (page cible en vue d'un traitement PHP).
Astuce : notez une balise LABEL vide afin que le bouton de commande se positionne au dessous des champs de saisie.

Depuis HTML5 les boutons de commande peuvent être créés avec la balise double BUTTON.
Notez pour le troisième input l'attribut readonly. Il est donc impossible de saisir dans ce champ !

Le formulaire, chaque INPUT et le bouton de commande sont nommés grâce à l'attribut name.
Ces noms seront utilisés dans le script.

Le script

Il est d'une simplicité déroutante !

f.bouton.onclick = function () {f.nom_prenom.value = f.nom.value +" "+ f.prenom.value}

Commentaire du script

Pour référence dans le script JS un élément de formulaire j'ai utilisé une bonne vieille technique mais toujours valide et reposant sur la syntaxe : nomFormulaire.nomChamp.

Je rajoute "value" quand je manipule le contenu d'un champ ou "onclick" pour appeler une fonction sur clic.
Notez que la fonction n'a pas de nom. On dit qu'il s'agit d'une fonction anonyme.
Testez ce code !

Inscription à un site

Nous allons maintenant aborder une thématique un peu plus complexe ... : l'inscription à un site.
Attention le traitement en PHP et SQL n'est pas traité ici.

Pour s'inscrire à un site vous devez saisir votre adresse mail puis la confirmer, saisir un mot de passe et le confirmer. Le mot de passe doit être composé uniquement de lettres majuscules et minuscules non accentuées (entre 6 et 8 lettres).
Nous voulons une vérification des données côté client.

Première solution (incorrecte)

Sans JS mais avec toutes les nouveautés HTML5 et CSS3

Le code correspondant

... <style> input:invalid {color : red; } input:valid {color : lime ; } </style> </head> <body> <h1>Inscription au site </h1> <form name ="f" action = "inscription_trait.php" method ='post'> <label>Tapez votre adresse mail : </label> <input type = "email" name = 'mail1' required placeholder = "n'oubliez pas @"> <label>Confirmez votre adresse mail : </label> <input type = "email" name = 'mail2' required> <label>Saisissez mot de passe :</label> <input type = "password" name = 'passe1' required pattern ='[A-z]{6,8}' maxlength ='8' placeholder = 'entre 6 et 8 lettres non accentuées'> <label>Confirmer le mot de passe : </label> <input type = "password" name = 'passe2' required pattern = '[A-z]{6,8}' maxlength ='8'> <label></label><button type = 'submit'> inscription</button> </form> ...

Commentaire

Examinons maintenant le code HTML !

<form action = "inscription_trait.php" method ='post'>

Sens : les données saisies sont envoyées dans la page "inscription_trait.php" avec la méthode POST

<input type = "email" name = 'mail1' required placeholder = "n'oubliez pas @">

Nous utilisons un nouveau type pour la balise input : email. Avec ce type la saisie sera valide uniquement si elle comprend le caractère @ !
Il y a aussi deux nouveaux attributs introduits par HTML5 : required et placeholder.
required : la soumission des données est bloquée tant que le champ est vide ou comprend une valeur incorrecte par rapport au type de champ ou au pattern (voir plus loin la notion de pattern).
placeholder : légende affichée dans le champ tant qu'il est vide

<input type = "password" name = 'passe1' required pattern ='[A-z]{6,8}' >

Ici on utilise l'attribut pattern avec pour valeur une expression régulière.

Une valeur incorrecte (contraire au pattern) bloque la soumission des données !

La balise button de est de type submit (on parle de bouton de soumission) pour pouvoir envoyer les données vers la page passée en valeur de l'attribut action.

Test

Saisissez dupont@free.fr puis dupond@free.fr puis aaaaaa puis bbbbbb
Testez ce code !

Vous arrivez à soumissionner les données puisque la page "inscription_trait.php" s'affiche et pourtant la saisie est fausse ...
En effet si séparément les quatre valeurs sont valides la deuxième adresse mail n'est pas identique à la première et le deuxième mot de passe n'est pas égal au premier !

Avec uniquement du HTML il est en effet impossible de comparer les valeurs de deux champs.
Pour avoir un contrôle de saisie efficace "côté client" il faut faire appel à JavaScript !

La bonne solution ???

Nous rajoutons un script !

Modification du code du formulaire

... <label></label><button type = 'button' name ="bouton"> inscription</button> ...

Le code HTML est inchangé (ou presque). Seule modification : la balise button devient de type button.
En effet si le bouton reste de type "submit" la soumission sera toujours effectuée dès que les contrôles de saisie prévus par HTML (required, pattern) sont OK et même si les contrôles de saisie JS ne sont pas vérifiés ...

Le script

f.bouton.onclick = function()
{
	// variables 
	var mail1 = f.mail1.value;
	var mail2 = f.mail2.value;
	var passe1 = f.passe1.value;
	var passe2 = f.passe2.value;
		// contrôle de saisie côté client 
	var vok = true ; 
	if(passe1.length < 6) {vok = false ; alert("mot de passe trop court") ; }
	if(mail1.indexOf('@') == -1) {vok = false ; alert("il manque l'@ dans adresse mail"); }
	if(mail1 != mail2) {vok = false ; alert("adresses mail différentes") ; }
	if(passe1 != passe2) {vok = false ; alert("mots de passe différents") ; }
	if (vok) {f.submit(); alert('envoi effectué') ; }
} //fin fonction anonyme

Donc plusieurs messages d'erreurs peuvent être affichés.
Par contre si vok reste à true alors les données sont soumissionnées grâce à la méthode submit() appliquée au formulaire.

Syntaxe

passe1.value ou mail1.value sont des chaines. On peut appliquer à une chaine des propriétés et des méthodes.
La propriété length appliquée à une chaine retourne le nombre de caractères de cette chaîne.
La méthode indexOf('souschaine') appliquée à une chaine retourne le rang de la sous chaîne dans la chaîne ou -1 si la sous chaîne n'a pas été trouvée.
Nous aurons l'occasion de revenir sur les propriétés et méthodes d'un objet de type string.

Tests

Je vais vous montrer qu'il y a encore une grosse faille de sécurité dans ce code à priori correct ...
Dans les champs "motdepasse" saisissez des espaces (à chaque fois 8 espaces). Testez ce formulaire d'inscription avec un script !

Vous accédez quand même à la page cible : inscription_trait.htm" ...

En effet comme le bouton est de type button les contrôles HTML (découlant des attributs required, pattern) ne sont plus effectués !

Solution définitive

Comment bénéficier simultanément des contrôles de saisie HTML et de ceux programmés ???
Il y a une astuce de codage que je vais évoquer maintenant.

Le code HTML

Il est modifié comme suit :

<form name ="f" action = "inscription_trait.htm" method ='post' onsubmit="return fenvoi()" > ... <button type ="submit"> inscription</button> ...

Le bouton de commande redevient de type "submi"t.
Dans la balise FORM j'ai rajouté : onSubmit="return fenvoi()"

Donc on bénéficie cette fois des contrôles de saisie HTML et JavaScript !!!

Le script

function fenvoi() { // variables var compteur =0 ; var mail1 = f.mail1.value; var mail2 = f.mail2.value; var passe1 = f.passe1.value; var passe2 = f.passe2.value; // test if(mail1 == mail2) compteur++; if(passe1 == passe2) compteur++; if (compteur < 2) {alert("champs mal renseignés");return false; } if (compteur==2) {alert("saisies correctes"); return true;} } // fin fonction

La variable compteur est incrémentée à chaque fois qu'un test est vérifié.

Si compteur < 2 la fonction retourne false et donc pas de soumission.
Si compteur ==2 (tous les tests vérifiés) la fonction retourne TRUE et donc soumission des donnés.

Tests

A vous!

Tant que les conditions HTML (champs non vides et respectant les "pattern") et les conditions du script (égalité stricte entre la saisie et la confirmation de saisie) alors soumission impossible !!!

Résumons nous

Pour bénéficier des contrôles HTML et de ceux prévus par la fonction JS il faut impérativement utiliser la syntaxe suivante :

... <form ... onsubmit="return nomfonction()" > ... <button type ="submit">... </button> ... <script> function nomfonction() { ... ... return true; ... return false ; } // fin fonction ...

Remarque importante

Un contrôle des saisies côté client (navigateur) ne dispense d'une vérification côté serveur (en PHP au niveau de la page cible).
En effet le JavaScript peut être désactivé par le navigateur.
Un utilisateur chevronné du Web peut accéder au code du formulaire et supprimer pour sa session les contrôles de saisie HTML.
En d'autres termes tous les contrôles côté client peuvent être inopérants.

le DOM

Le DOM (document Object Model) est une API JavaScript pour les documents HTML mais aussi XML. Grâce aux méthodes de cette API JavaScript nous allons pouvoir accéder aux éléments HTML de notre page pour les manipuler.

DOM : désormais normalisé

À l'origine, quand le JavaScript a été intégré dans les premiers navigateurs (Internet Explorer et Netscape), le DOM n'était pas unifié, chaque navigateur possédait un DOM différent. Donc pour référencer en JS un élément HTML, la manière différait d'un navigateur à l'autre, ce qui compliquait énormément la tâche des programmeur Web. On comprend pourquoi à cette époque le JavaScript avait une mauvaise réputation.

Le W3C a mis heureusement de l'ordre en publiant une spécification appelée « DOM-1 » (ou DOM Level 1) en 1998.
Cette nouvelle spécification définit clairement ce qu'est le DOM, et surtout comment un document HTML ou XML est structuré. Puis il y a eu le DOM-2 en 2000 et enfin le DOM-3 en 2004.

Donc grâce au DOM il est possible de produire du code indépendant du navigateur.

La structure DOM

Pour le DOM une page Web est une hiérarchie d'éléments. Prenons un exemple.

Code source d'une page

<!doctype html> <html> <head> <title>texte dans l'onglet</title> <meta charset ='utf-8'> </head> <body> <h1 id = 'titre'>Titre de la page</h1> <p id = 'texte'>Texte de la page</p> <p class ='rem'>Texte de la page - suite</p> <p>Texte de la page - fin</p> </body> </html>

La structure DOM de cette page

L'élément HTML comprend deux enfants : HEAD et BODY.
Pour ces deux enfants HTML est l'élément parent.
Un élément est appelé aussi noeud (node en anglais).
Le noeud HEAD contient deux enfants : TITLE et META.
Le noeud BODY contient quatre enfants : H1 et trois balises P.

Le noeud HTML correspond à la page ou document dans le jargon JS.

Des méthodes

L'API DOM ce n'est pas uniquement la représentation de la page sous forme d'une arborescence.
Le DOM c'est un ensemble de méthodes et propriétés pour accéder aux noeuds et les manipuler. Ainsi la fameuse méthode getElementById a été introduite par le DOM-2 pour accéder à un nœud en fonction de son ID unique.
Il est possible de créer des nœuds, supprimer des nœuds, modifier leur contenu, modifier leur style, modifier leurs attributs, etc.

Accéder aux noeuds

Dans le chapitre précédent pour référencer les éléments d'un formulaire j'ai utilisé une méthode antérieure au DOM.

Désormais vous pouvez utiliser les méthodes de l'objet document.

Ces nouvelles méthodes sont très pratiques car elles simplifient le code HTML ; les attributs ID et name deviennent facultatifs pour le traitement JS.

La méthode querySelector()

La méthode querySelectorAll()

Et si vous voulez référencer la troisième balise P de la page et que cet élément n'a ni l'attribut ID ni l'attribut class ???
Il suffit d'écrire : document.querySelectorAll('p')[2]
Notez que l'on a utilisé la méthode querySelectorAll() argumentée avec 'p' mais suivie d'un indice.
En effet la méthode querySelectorAll retourne non pas un noeud une collection de noeuds sous forme d'un tableau indicé. Donc il faut préciser l'indice pour cibler un seul élément !
Ici la valeur de l'indice est 2 (et non pas 3) car le premier élément de la collection a l'indice 0, la deuxième a l'indice 1 et le troisième élément a l'indice 2, etc.

Récupérer des informations sur les noeuds

Récupération des valeurs des attributs d'un noeud

Le code de la page (extraits)

... <style> .ombre {box-shadow : 5px 5px 5px grey ; } </style> </head> <body> <h1>Afficher les valeurs de tous les attributs d'une image </h1> <img src ='../images/toucan.jpg' alt ='toucan' title ='le toucan' width = '300' height = '200' class = 'ombre'> <script> var vinfo ; // première solution : noeud.getAttribute(attribut) var image = document.querySelector('img') ; vinfo = image.getAttribute('width'); alert('largeur image : ' + vinfo); vinfo = image.getAttribute('height'); alert('hauteur image: ' + vinfo); vinfo =image.getAttribute('src'); alert('source image: ' + vinfo); vinfo =image.getAttribute('alt'); alert('texte alternatif: ' + vinfo); vinfo =image.getAttribute('title'); alert('info bulle: ' + vinfo); vinfo =image.getAttribute('class'); alert('classe affectée : ' + vinfo) ; // deuxième solution : noeud.attribut alert('largeur image : ' + image.width); alert('hauteur image : ' + image.height); alert('source image : ' + image.src); alert('texte alternatif: ' + image.alt); alert('info bulle: ' + image.title); alert('classe affectée: ' + image.className); </script> </body> ... Testez cette page !

Commentaire du code

Ce script affiche deux fois la valeur de chacun des six attributs de l'image unique de la page). Ce script montre que pour récupérer les attributs d'un noeud il y a deux solutions :

Attention il y a cependant une différence avec l'attribut src qui indique le chemin relatif avec la syntaxe image.getAttribute('src') et indique le chemin absolu avec la syntaxe image.src

Pour récupérer le nom de la classe affectée à un noeud il faut argumenter la méthode getAttribute avec le mot class ; ce qui est des plus logiques. Par contre avec la syntaxe simplifiée il faut utiliser le mot className ce qui l'est moins ...

Récupération du contenu d'un noeud

Le code de la page (extrait)

... <body> <h1>Le pays d'<u>Ardres</u></h1> <img src = '../images/ardres.jpg'> <h2>Le lac d'Ardres en hiver</h2> <p><b>Ardres</b> est connu pour son lac qui est un havre de tranquillité</p> <script> // création de variables objet titre = document.querySelector('h1') ; // titre référence la balise h1 legende_image = document.querySelector('h2') ; // legende_image référence la balise h2 texte = document.querySelector('p') ; // texte référence la balise P alert(titre.innerHTML); // récupère tout le contenu du nœud alert(titre.textContent); // récupère uniquement le texte du nœud alert(texte.innerHTML); alert(texte.textContent); </script> </body> ... Testez cette page !

Commentaire

Pour récupérer le contenu d'un noeud on a deux propriétés : innerHTML et textContent.
Attention titre.innerHTML retourne : "Le pays d'<u>Ardres</u>" tandis que titre.textContent retourne : "Le pays d'Ardres".
Donc la première propriété retourne tout le contenu d'un noeud (y compris le balisage HTML dans le noeud) tandis que la deuxième retourne uniquement le texte du noeud (sans le balisage interne).

La récupération des valeurs des propriétés de style

Dans certains ouvrages vous pourrez lire que pour récupérer la valeur d'une propriété de style d'un noeud il faut utiliser la syntaxe : noeud.style.propriété

N'utilisez surtout pas cette syntaxe !
En effet ça ne marche que si les propriétés de style du noeud sont définis dans la balise donc via l'attribut style.
Avec cette syntaxe il n'est donc pas possible de récupérer une propriété de l'élément définie dans le cadre d'une feuille de style(interne ou externe).

Le code de l'exemple

... <link href ="../style.css" rel = "stylesheet" > <style> h1 {background-color : yellow ; height : 50px ; line-height : 50px ; border : 2px solid red ; } </style> </head> <body> <h1>Titre de la page</h1> <img src ='../images/toucan.jpg' width = '200' style = "margin : 20px auto 20px auto ; opacity : 0.5; border : 1px solid black " > <button>infos sur mise en forme image</button> <button>infos sur mise en forme titre </button> <script> // variables objet globales pour référence deux noeuds. var titre = document.querySelector('h1') ; var image = document.querySelector('img'); document.querySelector('button').onclick = function() { var vinfo = getComputedStyle(image).opacity ; alert("valeur de opacity : " + vinfo); var vinfo = getComputedStyle(image).border ; alert("valeur de border : " + vinfo); var vinfo = getComputedStyle(image).marginLeft ; alert("valeur de margin left : " + vinfo); var vinfo = getComputedStyle(image).marginTop ; alert("valeur de margin top : " + vinfo); var vinfo = getComputedStyle(image).display ; alert("valeur de display : " + vinfo); } document.querySelectorAll('button')[1].onclick = function() { vinfo = getComputedStyle(titre).backgroundColor ; alert("valeur de background-color : " + vinfo); vinfo = getComputedStyle(titre).height ; alert("valeur de height : " + vinfo); vinfo = getComputedStyle(titre).lineHeight ; alert("valeur de line-height : " + vinfo); vinfo = getComputedStyle(titre).border ; alert("valeur de border : " + vinfo); vinfo = getComputedStyle(titre).fontSize ; alert("valeur de font-size : " + vinfo ); vinfo = getComputedStyle(titre).marginTop ; alert("valeur de margin top : " + vinfo ); } </script>

Testez ce code !

Commentaire

J'ai utilisé la méthode getComputedStyle(noeud).propriété

Bien sûr je désigne par "noeud" la variable objet qui référence le noeud.

Vous pouvez remarquer que j'ai récupéré des valeurs de propriétés de style quelque soit l'endroit où elles sont définies.
Ainsi pour le noeud image la propriété opacity est définie dans la balise mais la propriété display est définie dans la feuille de style externe ("style.css"). Et pour le noeud titre les propriétés sont définies dans la feuille de style interne.

Attention vous devez utiliser la syntaxe JavaScript des propriétés de style (et non pas la syntaxe CSS) !

Il y a une différence lorsque la propriété est un mot composé.
En effet en JavaScript les tirets sont interdits(signifie "moins").
Donc pour passer de la syntaxe CSS à la syntaxe JS il suffit de supprimer le tiret et de remplacer la lettre qui suit par son équivalent en majuscule.

Prenons quelques exemples.

propriété de formatagesyntaxe CSSsyntaxe JS
couleur de fondbackground-colorbackgroundColor
bordure (méga propriété)borderborder
hauteur de ligneline-heightlineHeight
marges externesmarginmargin

Modifier les noeuds

Dans le chapitre précédent nous avons vu comment récupérer des informations sur un noeud (attribut, contenu, mise en forme).
Mais pour obtenir des pages dynamiques c'est à dire qui se modifient à l'initiative du visiteur ou de façon aléatoire il faut que le script modifie le contenu / valeurs d'attributs /valeurs des propriétés de style des noeuds.

Exemple sexy

Ci-dessous l'image change lorsque vous cliquez sur le bouton de commande et ce dernier disparait.
Grâce à JS la valeur de l'attribut scr de l'image est changée et la valeur de la propriété display du bouton est modifiée. burkini

Modifier les valeurs des attributs des éléments

Dans cet exemple je modifie les attributs width et height d'un noeud (une image).

Le code de la page (extrait)

... <body> <h1>Modification de deux attributs d'un noeud : width et height</h1> <p>Le rapport largeur / hauteur est toujours égal à 3/2</h1> <img src ='../images/toucan.jpg ' width = '300' height ='200' style = 'margin : auto ;'> <table> <caption>Faites varier la taille de l'image</caption> <tr><th><button id = 'plus'>+</button></th><th> <button id = 'moins'>-</button></th></tr> </table> <script> var image = document.querySelector('img'); var vlargeur = 300; var vhauteur; document.querySelector('#plus').onclick = function() { vlargeur +=9 ; vhauteur = vlargeur *2 /3 ; image.width = vlargeur ; image.height = vhauteur ; } document.querySelector('#moins').onclick = function() { vlargeur -=9 ; vhauteur = vlargeur *2 /3 ; image.setAttribute('width',vlargeur) ; image.setAttribute('height',vhauteur) ; } </script> </body> ... Testez l'exemple !

Commentaire

Il y a une image redimensionnée par rapport à sa taille d'origine grâce aux attributs width et height.
Il y a un clavier tactile obtenu grâce à un tableau HTML. Ce tableau comprend dans chaque cellule un bouton. Un bouton est identifié plus et un autre moins.
Si vous cliquez sur le bouton plus vous augmentez la taille de l'image et si vous cliquez sur moins vous réduisez sa taille.

Dans la première fonction on utilise la syntaxe simplifiée noeud.attribut= nouvelle valeur et dans la deuxième fonction on utilise la méthode setAttribute.
Donc la syntaxe est : noeud.setAttribute('attribut',nouvelle valeur )

Modifier le contenu d'un noeud

Dans cet exemple je modifie le contenu de plusieurs noeuds.

Le code de la page (extraits)

... <body> <h1>Modification des contenus des éléments</h1> <h2>Le pays d'Ardres</h2> <figure> <img src = '../images/ardres.jpg'> <figcaption>Le lac d'Ardres en hiver</figcaption> </figure> <p>Ardres est connu pour son lac qui est un havre de tranquillité.</p> <img src ='../images/francais.jpg' class ='miniature' title = 'version française'> <img src ='../images/anglais.jpg' class ='miniature'title = 'version anglaise'> <script> // création de variables objet titre = document.querySelector('h2') ; // titre référence la balise h1 legende_image = document.querySelector('h3') ; // legende_image référence la balise h2 texte = document.querySelector('p') ; // texte référence la balise P // gestion événements document.querySelectorAll('img')[1].onclick = fran; // appel de la fonction fran si clic sur la deuxième image document.querySelectorAll('img')[2].onclick = angl ; // appel de la fonction angl si clic sur la troisième image // ci-dessus gestion événements function fran() { // page en français titre.textContent = "Le pays d'Ardres" ; texte.innerHTML = "<b>Ardres est connu pour son lac qui est un havre de tranquillité.</b>"; legende_image.textContent = "Le lac d'Ardres en hiver"; } // fin fonction function angl() { // page en anglais titre.textContent = "Country of Ardres" ; texte.innerHTML = "<b>Ardres is known for his lake which is a place of tranquillity.</b>"; legende_image.textContent = "the lac of Ardres during winter"; } // fin fonction </script> ... Testez !

Commentaire

En cliquant sur la deuxième image (drapeau tricolore) on appelle la fonction fran qui remplit chaque noeud avec un texte français.
En cliquant sur la troisième image (union jack) on appelle la fonction angl qui remplit chaque noeud avec un texte anglais.

Attention pour le noeud texte on utilise innerHTML (et non pas textContent) pour modifier non seulement le texte mais aussi le balisage ; le texte de ce noeud sera donc en gras !

Modifier les valeurs des propriétés des éléments

Le code de la page

... <style> img{margin : 20px auto 20px auto ; opacity : 0.1;} </style> </head> <body> <h1>Modification d'une propriété de mise en forme</h1> <p>La valeur de la propriété de style <b>opacity</b> augmente ou diminue selon que l'on clique sur + ou sur - </p> <img width = '200' src ='../images/toucan.jpg' > <table> <caption>Faites varier l'opacité de l'image</caption> <tr><th><button id = 'plus'>+</button></th><th> <button id = 'moins'>-</button></th></tr> </table> <script> var image = document.querySelector('img'); var vopacite = getComputedStyle(image).opacity ; // récupération de la valeur actuelle de la propriété opacity alert("type de la valeur :" + typeof(vopacite)) ; // attention la valeur retournée par getComputedStyle est de type string var vopacite = parseFloat(vopacite); // conversion d'une chaîne en réel document.querySelector('#plus').onclick = function() { vopacite +=0.1; image.style.opacity = vopacite ;} document.querySelector('#moins').onclick = function() { vopacite -=0.1; image.style.opacity = vopacite ;} </script> ... Testez !

Commentaire

La page comprend une image pratiquement transparente (opacity = 0,1) et un clavier tactile.
Cette quasi transparence a été définie au niveau de la feuille de style interne.

Le script consiste à modifier la valeur de la propriété de style opacity.
Mais il faut d'abord récupérer la valeur initiale avec la méthode getComputedStyle (voir chapitre précédent) dans la variable vopacite.
Or la valeur récupérée est de type string comme l'indique la fonction typeof.
On ne peut incrémenter ou décrémenter directement une chaîne ! Donc il faut avant la convertir en réel avec la fonction parseFloat.
En cliquant sur le bouton plus l'image devient plus opaque puisque on incrémente vopacite .
En cliquant sur le bouton moinsl'image devient plus transparente puisque on décrémente vopacite.

Pour changer la valeur d'une propriété de style vous pouvez toujours utiliser la syntaxe :
noeud.style.propriété = nouvelle valeur

Ajouter un attribut à un élément

A l'occasion de clic sur des boutons on ajoute l'attribut class à différents noeuds.

Le code de la page (extraits)

... <style> section {width : 900px ; height : 600px ; border : 2px solid black ; margin : auto ; } .marine {background : aqua ; color : navy ; border-color : navy ; } .barbie {background : pink ; color : purple ; border-color : purple; } .gothique {background : black ; color : red ;border-color : red ; } ... <body> <section> <h1>Changer la classe affectée à chaque noeud</h1> <p>Au départ aucune classe n'est affectée aux trois noeuds : section, h1, p <br>Puis en fonction du bouton cliqué vous affectez la classe <b>marine</b> ou la classe <b>barbie</b> ou la classe <b>gothique</b> à chacun de ces trois noeuds</p> <button>style marine </button> <button>style barbie </button> <button>style gothique </button> </section> <script> // variables objetq globales var page = document.querySelector("section"); var titre = document.querySelector('h1'); var texte = document.querySelector('p'); var vnomclasse ; document.querySelector('button').onclick = function() { vnomclasse ='marine' page.className = vnomclasse; titre.className = vnomclasse; texte.className = vnomclasse; } // fin fonction document.querySelectorAll('button')[1].onclick = function() { vnomclasse ='barbie'; page.className = vnomclasse; titre.className = vnomclasse; texte.className = vnomclasse; } // fin fonction document.querySelectorAll('button')[2].onclick = function() { page.className = 'gothique'; titre.className = 'gothique'; texte.className = 'gothique'; } // fin fonction </script> ...
Testez !

Commentaire

Au départ aucune classe n'est affectée aux différents éléments.
En cliquant sur le premier bouton le visiteur ajoute class ="marine" aux trois noeuds, en cliquant sur le deuxième bouton c'est alors la classe barbie et en cliquant sur le troisième bouton c'est alors la classe gothique.

J'ai utilisé la syntaxe simplifié pour ajouter l'attribut class : noeud.className = nomclasse

Oui vous avez bien lu pour changer la classe affectée à un noeud il faut utiliser le mot className et non pas class. C'est pas très logique mais c'est ainsi ...

J'aurais pu aussi utiliser la méthode setAttribute ; Ce qui aurait donné des instructiones telles :
page.setAttribute('class',vnomclasse).

ClassName est l'exception qui confirme la règle. Pour tous les autres attributs le mot à employer est identique à celle utilisée en HTML !
Exemple : pour ajouter l'attribut alt la syntaxe simplifiée est : noeud.alt="texte"

Exercice : affichez les images ou masquer les images de la page

Dans une page les images sont par défaut chargées mais masquées.
En cliquant sur un bouton le visiteur les affiche. En cliquant sur un autre bouton le visiteur les masque de nouveau. Rendu à obtenir !

Le code (extraits) de l'exemple à compléter

... <style> img {display : none ; height : 200px ; } </style></head> <body> <h1>Un bel album photos </h1> <img src = '../images/toucan.jpg'> <h2>Le toucan</h2> <img src = '../images/tortue.jpg'> <h2>La tortue</h2> <img src = '../images/foret.jpg'> <h2>La forêt</h2> <button type ='button'>afficher images</button> <button type ='button'>masquer images</button> <script> var images = ... ; // images est un tableau référençant toutes les images var vnombre = ... ; // vnombre indique le nombre d'éléments du tableau images document.querySelector('button').onclick = function() { // parcours du tableau images pour les afficher for(... ) { ... = 'block' ; } } // fin fonction ... = function() { // parcours du tableau images pour les masquer de nouveau for(... ){... = 'none' ; } } // fin fonction </script> ...

Aide

Grâce au CSS les images chargées sont par défaut masquées grâce au CSS : img{display : none ; }
Donc pour les démasquer il faut que pour chaque image la valeur de la propriété display passe à block !
Et pour les masquer de nouveau il faut que display repasse à none.
Il s'agit donc d'un changement de valeur d'une propriété de style pour une collection d'objets.

Correction

Le script de la page :

<script> var images = document.querySelectorAll('img') ; // images est un tableau référençant toutes les images var vnombre = images.length ; // nombre d'éléments du tableau images document.querySelector('button').onclick = function() { for(i = 0 ; i<= vnombre ; i++) { images[i].style.display = 'block' ; } } // fin fonction document.querySelectorAll('button')[1].onclick = function() { for(i = 0 ; i<= vnombre ; i++){images[i].style.display = 'none' ; } } // fin fonction </script>

Le principe est très simple.
Pour afficher les images il suffit de faire passer à "block" la propriété "display".
Pour masquer de nouveau les images il faut que "display" repasse à "none".

Initiation aux animations

Maintenant que nous savons modifier les attributs, propriétés de style et contenus des noeuds nous pouvons réaliser de petites animations sympathiques dans une page web ...

Animation contrôlée par l'internaute

L'internaute déplace un gif animé dans une boîte avec en fond un beau décor.


Essayez !

Le code correspondant

... <body> <div id = 'decor'style = "display : block ; width : 600px ; height : 400px ; position : relative ; background : url(../images/fond3.jpg) ; margin : auto ; " > <img id ='requin' src ='../images/requin.gif' style = "position : absolute ; top : 0px; left : 0px ; " > </div> <table> <caption>Déplacer le squale</caption> <tr><th></th><th><button id ='haut' >&uarr;</button></th><th></th></tr> <tr><th><button id ='gauche'>&larr;</button></th><th></th><th><button id ='droite'>&rarr;</button></th></tr> <tr><th></th><th><button id = 'bas'>&darr;</button></th><th></th></tr> </table> <script> var requin = document.querySelector('#requin') ; x = getComputedStyle(requin).left ; y = getComputedStyle(requin).top ; alert(x) ; alert(y); document.querySelector('#droite').onclick = function() { x = parseInt(x) ; x+=20 ; x = x +'px' ; requin.style.left = x ; } document.querySelector('#gauche').onclick = function() { x = parseInt(x) ; x -= 20 ; x = x +'px' ; requin.style.left = x ; } document.querySelector('#haut').onclick = function() { y = parseInt(y) ; y-=20; y = y +'px' ; requin.style.top = y ; } document.querySelector('#bas').onclick = function() { y = parseInt(y) ; y+=20; y = y +'px' ; requin.style.top = y ;} </script> ...

Commentaire du code

On déplace une image (le requin) dans une boîte en faisant varier les valeurs des propriétés top et left dans le cadre d'un positionnement absolu.

Commentaire du HTML

Commentaire du code JavaScript

Attention la valeur des propriétés de style top (et/ou left) doivent être une chaine telle "50px"

Grâce à la méthode getComputedStyle le positionnement initial du gif animé est récupéré.
Comme l'indique les instructions de débuggage basées sur la méthode alert les variables x et y contiennent chacune une chaine ("0px").

Nous avons ensuite quatre fonctions pour déplacer l'image. A chaque touche du clavier tactile est associé une fonction sur l'évènement clic (onclick).
Dans chaque fonction il faut convertir le contenu de x (ou y) en un entier (se débarrasser de "px") puis il faut faire incrémenter (ou décrémenter) x (ou y) puis il faut rajouter "px" à x (ou y) et enfin il modifier la valeur des propriétés left et top.

Le requin peut sortir du décor car ici les "effets de bord" ne sont pas gérés.

Animation automatique

Le gif animé (un plongeur) se déplace automatiquement dans la boîte.


Essayez !

Le code correspondant

... <body> <div id = 'decor'style = "display : block ; width : 600px ; height : 400px ; position : relative ; background : url(../images/fond3.jpg) ; margin : auto ; " > <img id ='plongeur' src ='../images/plongeur3.gif' style = "position : absolute ; top : 350px; left : 500px ; " > </div> <button>Arrêter animation</button> <script> var plongeur = document.querySelector('#plongeur') ; x = getComputedStyle(plongeur).left ; y = getComputedStyle(plongeur).top ; x_maxi = 500; y_maxi = 350; // l'image fait 100 par 50 var deltax = 3 ; var deltay = 2 ; // car la zone fait 600 par 400 donc un rapport 3/2 var boucle = setInterval(fdeplacement,50) ; document.querySelector('button').onclick = function() {clearInterval(boucle); } function fdeplacement() { // exéctué toutes les 50 millisecondes donc x = parseInt(x) ; y = parseInt(y) ; if(x > x_maxi || y > y_maxi) {deltax = -deltax ; deltay = -deltay ;} if(x < 0 || y < 0 ) {deltax =-deltax ; deltay =-deltay ; } x+=deltax ; y+=deltay ; x = x+'px' ; y = y+'px' ; plongeur.style.left = x ; plongeur.style.top = y ; } </script> ...

Commentaire du code

Du HTML

Comme dans la page précédente un gif animé (ici un plongeur) est positionnée en absolu dans une boîte DIV.

Du JavaScript

Comme dans la page précédente on utilise les variables x et y mais aussi x_maxi, y_maxi, deltax, deltay.
x_maxi et y_maxi réprésentent les valeurs maximales de left et top pour le noeud "plongeur".
Le gif animé fait 100 par 50. Nous ne voulons pas que le plongeur sorte du décor qui fait 600 par 400 donc on règle x_maxi à 500 (600-100) et y_maxi à 350 (400-50).

var boucle = setInterval(fdeplacement, 50) : signifie que la fonction fdeplacement est exécutée selon une fréquence de 50 millisecondes. La répétition est identifiée par la variable boucle.
clearInterval(boucle) : pour mettre fin à la répétition "boucle".

Mais en quoi consiste la fonction fdeplacement ?
Le plongeur doit se déplacer alternativement vers la gauche et le haut puis vers la droite et le bas sans quitter le décor.
Les variables x, y doivent donc alternativement diminuer puis augmenter.
Donc les variables deltax, deltay doivent alternativement être négatives puis positives.
deltax = -deltax et deltay = -deltay : grâce à ces commandes les variables deltay et deltay changent de signe.
deltax, deltay changent de signe lorsqu'il y a un "effet de bord".
Dès que les bords droit ou bas sont atteints ces variables deviennent négatives.
Dès que les bords gauche ou haut sont atteints ces variables redeviennent positives.

Encore une animation

Les deux premières animations consistaient à modifier les valeurs des propriétés CSS top et left (dans le cadre du positionnement absolu).

Ici l'animation consiste à faire varier automatiquement la taille d'une image gif dans une boîte. Hors les dimensions de l'image sont ici des attributs et non pas des propriétés de style donc il faut utiliser la méthode setAttribute.
Testez cette animation !

Le code de la page (extraits)

... <div id = 'decor' style = "display : block ; width : 600px ; height : 600px ; position : relative ; ; margin : auto ; border : 1px solid black ; " > <img id ='train' height = '100' src ='../images/train.gif' style = "position : absolute ; top : 0px; left : 0px ; " > </div> <script> var train = document.querySelector('#train') ; var cote = 100 ; var variation = 10 ; setInterval(fvariation,100) ; function fvariation() { // exécutué toutes les 50 millisecondes cote +=variation ; // cote incrémentée ou décrémentée de 10 if(cote >= 500) variation = -10 ; if(cote <=100) variation = 10 ; // aucune conversion nécessaire car unité de mesure implicite est px train.setAttribute('height', cote); } </script> ...

Commentaire du code JavaScript

Il est beaucoup plus simple que celui des animations précédentes car ici on n'a pas à faire de conversions !
On peut en effet, affecter directement un entier à l'attribut height (ou width) d'un noeud puisque l'unité est implicite.

Les événements

Gestion des événements

Une fonction est exécutée lorsque survient un événement sur un noeud. A la lecture des pages précédentes de ce tutoriel vous pouvez penser à juste titre qu'il n'y a qu'un événement : clic (click en anglais).
Et bien non il y a d'autres événements. Citons focus, blur, change, load, mouseover, unload, etc.

Dans le cadre de gestion des événements on peut utiliser (ou pas) l'API DOM.

Prenons un exemple. Nous voulons qu'une fonction JavaScript s'exécute lors d'un clic sur un lien hypertexte. La fonction demande votre nom puis l'affiche précédé de "bonjour".

Je vous propose trois solutions pour ce thème. la première sans le DOM, la deuxième avec le DOM-0 et la troisième avec le DOM-2.

Première solution : sans le DOM

... <body> <a href ='#' onclick ="bonjour()" >Appel fonction </a> <script> function bonjour() { var vnom = prompt('tapez votre nom') ; alert("bonjour " + vnom );} </script> ...

On utilise le gestionnaire d'événement de HTML (et non pas le DOM).
Inconvénient : on retrouve dans une instruction HTML du JavaScript.
Essayez !

Utilisation du DOM-0

... <body> <a href ='#' >Appel fonction </a> <script> document.querySelector('a').onclick = function() { var vnom = prompt('tapez votre nom') ; alert("bonjour " + vnom )} </script> ...
Essayez !

On utilise l'API DOM et plus précisément la méthode querySelector de l'objet document.
L'emploi de l'API DOM permet de bien séparer le code HTML du code JS et aussi d'utiliser la technique de la fonction anonyme.

L'emploi du DOM pour gérer les évènements offre aussi un autre avantage : possibilité d'utiliser l'objet event que nous évoquerons bientôt.

Utilisation du DOM-2

... <body> <a href ='#' >Appel fonction </a> <script> var lien = document.querySelector('a'); lien.addEventListener('click',function() {var vnom = prompt('tapez votre nom') ; alert("bonjour " + vnom );},false); </script> ...
Essayez !

Observez bien le script. Dans le cadre du DOM-2 il faut utiliser la méthode addEventListener avec trois arguments:

Avantage du DOM-2

L'emploi du DOM-2 est un peu plus compliqué. Mais il offre un gros avantage : possibilité de pouvoir associer à un même évènement sur le même noeud plusieurs fonctions. C'est le cas dans l'exemple ci-dessous :

... <a href ='#' >Appel fonction </a> <script> var vnom,vprenom ; // variables globales var lien = document.querySelector('a'); lien.addEventListener('click',function() {vnom = prompt('tapez votre nom') ;vprenom = prompt('tapez votre prénom') },false); lien.addEventListener('click',function() {alert("bonjour " + vnom );alert("prénomé " + vprenom )},false); </script>

On associe au noeud lien sur clic deux fonctions : une pour la saisie et une autre pour l'affichage de variables globales.
Essayez !

Les événements focus et blur

Emploi du DOM-0 !

Le code de l'exemple (extrait)

... <body> <form> <label>Note ? </label><input type = 'text'> <label>Libellé</label><input type = 'text'> </form> <script> document.querySelector('input').onfocus = function() {this.value ='le séparateur décimal est le point !' ; } document.querySelector('input').onblur = function() {if(isNaN(this.value)) alert('erreur de saisie') ; else alert('saisie correcte'); } </script> ... Essayer !

Commentaire du code

Grâce à ce script il y a un contrôle de saisie sur le premier input du formulaire.

Nous voyons une bonne illustration de deux événements liés aux champs de formulaire : focus et blur.
Ici un message ('le séparateur décimal ...) s'affiche dès que le curseur pointe le premier input (onfocus). Dès que cette zone de saisie n'est plus pointée (onblur) un test est effectué pour afficher un second message "erreur de saisie" ou "saisie correcte".
Notez l'emploi du mot this qui est très pratique et qui fait référence à l'objet en cours d'utilisation ; ici le premier input.
Donc le mot this remplace ici : document.querySelector('input').

L'objet "Event"

Lorsque vous cliquez sur un noeud ou survolez un noeud ou encore lorsque vous appuyez sur un touche du clavier un évènement se produit c'est à dire un changement d'état qui peut être analysé par JavaScript.

La compréhension de l'objet Event est indispensable dans le cadre de l'animation via le clavier.

Il y a d'autres propriétés mais qui sont moins utiles ...

Exemple

Le code de l'exemple (extrait)

... <body> <button>Cliquez ce bouton de commande et observez ! </button> <img src ="../images/toucan.jpg" height ="200" > <p>Cliquez l'image et observez !</p> <h2>Appuyez sur n'importe quelle touche du clavier et observez ! </h2> <p>En particulier appuyez sur les touches &larr; , &uarr; , &rarr; et &darr; du clavier et notez sur un papier le code ASCII de chacune de ces quatre touches. <br>Ainsi vous comprendrez la programmation du clavier.</p> ... <script> document.onkeydown = function(event) { // si appui sur une touche du clavier alert("code ASCII de la touche : " + event.keyCode) ; alert("type évènement : " + event.type) ; alert("cible évènement : " + event.target) ; } document.querySelector('button').onclick = function(evt) { alert("type évènement : " + evt.type) ; alert("cible évènement : "+ evt.target); } document.querySelector('img').onclick = function(e) { alert("type évènement : " + e.type) ; alert("cible évènement : " + e.target); } </script> ... Testez !

Commentaire

Attention la propriété type retourne click (et non pas onclick), mouseover (et non pas onmouseover), keydown (et non pas onkeydown) , etc.

L'évènement est pour JavaScript un objet. Mais c'est au programmeur de trouver un nom à cet objet. Souvent il est nommé event ou encore e ou plus rarement evt.

Dans la première fonction j'ai nommé l' objet évènement event, e dans la deuxième fonction et enfin evt dans la troisième fonction.

Si vous avez suivi les consignes vous avez remarqué que les touches de direction du clavier ont un code ASCII compris entre 37 et 40 inclus.

Programmation du clavier

Maintenant que vous connaissez l'objet Event et l'une des sa propriété keyCode (code ASCII de la touche appuyée) vous devriez comprendre le code du jeu "les dents de la mer" qui repose sur la programmation de certaines touches du clavier.

Il s'agit d'un jeu certes "bébête" mais l'intérêt est que sa programmation est désormais à votre niveau (lol) !
Il se joue à deux !
Un joueur déplace le requin avec les touches "gauche" "droite" "haut" "bas" du clavier pour tenter de croquer le plongeur.
L'autre joueur déplace le plongeur avec les touches U (up) D (down) L (left) et R (right) du clavier pour échapper aux dents du requin.

Le code de la page (extraits)

... <body> ... <div id = 'decor' style = "display : block ; width : 600px ; height : 400px ; position : relative ; background : url(../images/fond3.jpg) ; margin : auto ; " > <img id ='requin' src ='../images/requin.gif' style = "position : absolute ; top : 10px; left : 20px ; " > <img id ='plongeur' src ='../images/plongeur3.gif' style = "position : absolute ; top : 350px; left : 500px ; " > </div> <script> var requin = document.querySelector('#requin') ; var plongeur = document.querySelector('#plongeur') ; var zone = document.querySelector('#decor'); X = zone.style.width ; // pour récupérer la largeur de la boîte X = parseInt(X) ; // conversion en entier Y = zone.style.height ; Y = parseInt(Y) ; x_maxi = X-100 ; y_maxi = Y-70; var xr = requin.style.left; var yr = requin.style.top; var xp = plongeur.style.left; var yp = plongeur.style.top; document.onkeydown = fclavier_lire; // fonction appelée à chaque fois qu'une touche du clavier est enfoncée function fclavier_lire(e) { // déplacement requin if (e.keyCode == 37) gauche_r(); if (e.keyCode == 39) droite_r(); if (e.keyCode == 38) haut_r(); if (e.keyCode == 40) bas_r(); // déplacement plongeur if (e.keyCode == 76) gauche_p(); if (e.keyCode == 82) droite_p(); if (e.keyCode == 85) haut_p(); if (e.keyCode == 68) bas_p(); } // fonctions pour déplacement du requin function gauche_r() { xr = parseInt(xr) ; xr = xr - 6 ; if( xr <= 0) xr = 0; xr = xr +'px' ; requin.style.left = xr ; } function droite_r() { xr = parseInt(xr) ; xr = xr + 6 ; if (xr >= x_maxi) xr = x_maxi ; xr = xr +'px' ; requin.style.left =xr ;} function haut_r() { yr = parseInt(yr) ; yr= yr - 6; if (yr <= 0) yr = 0 ; yr = yr +'px' ; requin.style.top = yr ; } function bas_r() { yr = parseInt(yr) ; yr= yr + 6; if (yr >= y_maxi) yr = y_maxi ;yr = yr +'px' ; requin.style.top = yr ; } // fonctions pour déplacement du plongeur function gauche_p() { xp = parseInt(xp) ; xp = xp - 10 ; if( xp <= 0) xp = 0; xp = xp +'px' ; plongeur.style.left = xp ; } function droite_p() { xp = parseInt(xp) ; xp = xp + 10 ; if (xp >= x_maxi) xp = x_maxi ; xp = xp +'px' ; plongeur.style.left =xp ;} function haut_p() { yp = parseInt(yp) ; yp= yp - 10; if (yp <= 0) yp = 0 ; yp = yp +'px' ; plongeur.style.top = yp ; } function bas_p() { yp = parseInt(yp) ; yp= yp + 10; if (yp >= y_maxi) yp = y_maxi ;yp = yp +'px' ; plongeur.style.top = yp ; } </script> ... Jouez !

Commentaire du code

Le code CSS et le code HTMl ne présentent aucune difficulté.
Un gif (le requin) est positionné en haut à gauche de la boîte DIV et un autre gif (le plongeur est positionné en bas à droite).

Par contre le JavaScript présente une grande nouveauté : la programmation des touches du clavier.
document.onkeydown = fclavier_lire: si frappe d'une touche du clavier alors appel de la fonction fclavier_lire.
function fclavier_lire(e): le paramètre e représente l'évènement.

if (e.keyCode == 37) gauche_r() : Chaque touche a un code ; la touche ← a le code 37. Donc si appui sur la touche ← appel de la fonction gauche_r

Ces fonctions ne présentent aucune difficulté si vous avez compris le code de la page "animation1.htm" et "animation2.htm".


Retour menu