Dynamisez vos pages web avec JavaScript

Digression CSS3 : vous pouvez remarquer que dans ce tutoriel les titres sont numérotés automatiquement !
Pour davantage d'explications sur la numérotation automatique des titres via le CSS voir le "tutoriel CSS3" dans ce site !

Les différents chapitres

Utiliser les formulaires HTML et JavaScript Le DOM Modifiez les noeuds du DOM Initiation aux animations avec JavaScript Les événements JavaScript

Utiliser les formulaires HTML conjointement avec JavaScript

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

Or dans HTML nous avons un outil formidable : le formulaire. Et c'est très simple de manipuler en JS les champs et boutons d'un formulaire.

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 ; } </style> </head> <body> <h1>Nom et prénom saisis dans deux champs distincts d'un formulaire <br>Les deux concaténés dans un troisième champ </h1> <form> <label>votre nom : </label> <input type = 'text'> <label>votre prénom : </label> <input type = 'text'> <label></label> <button type = 'button'>Concaténez ! </button> <label>Nom et prénom : </label> <input type ='text' readonly> </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 boîtes (à qui on peut affecter les propriétés height, width, margin, etc) mais des boîtes sans saut de ligne entre elles et qui donc se positionnent côte à côte (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 boîte parente (form). Donc il y aura deux contrôles par ligne (la légende du champ et le champ de saisie).

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 saisie en vue d'un traitement en PHP !
Remarquez une balise label vide afin que le bouton de commande se positionne au dessous des champs de saisie.
Le bouton est de type button afin de pouvoir y associer une procédure JS.
Notez pour le troisième input l'attribut readonly. Il est donc impossible de saisir dans ce champ !

Le script

		nom = document.querySelector('input') ; 
		prenom  = document.querySelectorAll('input')[1] ; 
		nom_prenom  = document.querySelectorAll('input')[2] ; 
		document.querySelector('button').onclick = function () 
			{nom_prenom.value = nom.value + ' ' + prenom.value ; }

Commentaire du script

On crée d'abord des variables objets.
Une variable objet ne contient pas une valeur mais désigne dans le script un élément HTML de la page.

Pour référencer dans le script des éléments du formulaire HTML j'ai utilisé deux méthodes de l'objet document : querySelector() et querySelectorAll(). Je n'en dit pas plus pour le moment !
Les explications sur ces méthodes seront données dans le chapitre suivant : le DOM.

La fonction anonyme :
document.querySelector('button').onclick = function() : si clic sur le bouton alors exécution d'une fonction anonyme.
Cette fonction anonyme ne comprend qu'une instruction.
nom_prenom.value = nom.value + ' ' + prenom.value : Les contenus des champs nom et prenom sont concaténés et deviennent le contenu du champ nom_prenom !

Testez ce code !

Inscription à un site

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 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 (correcte)

Un contrôle de saisie en JS !

Le code de la page (extraits)

... <label></label><button type = 'button'> inscription</button> </form> <script> document.querySelector('button').onclick = function() { // création des variables objet var mail1 = document.querySelector('input'); // référence le premier input var mail2 = document.querySelectorAll('input')[1]; // référence le deuxième input var passe1 = document.querySelectorAll('input')[2]; var passe2 = document.querySelectorAll('input')[3]; var formulaire = document.querySelector('form'); // référence le formulaire unique // contrôle de saisie côté client var vok = true ; if(passe1.value.length < 6) {vok = false ; alert("mot de passe trop court") ; } if(mail1.value.indexOf('@') == -1) {vok = false ; alert("il manque l'@ dans adresse mail"); } if(mail1.value != mail2.value) {vok = false ; alert("adresses mail différentes") ; } if(passe1.value != passe2.value) {vok = false ; alert("mots de passe différents") ; } if (vok) {formulaire.submit(); alert('envoi effectué') ; } } //fin fonction anonyme </script> ...
Testez !

Commentaire du code

Le code HTML est inchangé (ou presque). Seule modification : la balise button devient de type button afin de lui associer une procédure JS.

Le code JS :
Chaque input est référencé par une variable objet. La variable formulaire référence le formulaire unique de la page.

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 chaînes. On peut appliquer à une chaîne des propriétés et des méthodes.
La propriété length appliquée à une chaîne retourne le nombre de caractères de cette chaîne.
La méthode indexOf('souschaîne') appliquée à une chaîne 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.

Remarque

Le contrôle de saisie côté client (avec du JS) ne dispense pas de faire un contrôle de saisie côté serveur (en PHP) car n'oubliez jamais qu'il est possible pour un utilisateur averti de désactiver le JS.

le DOM

Définition

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 (ou SVG) de notre page pour les manipuler.

À 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 répré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 noeud en fonction de son ID unique.
Il est possible de créer des noeuds, supprimer des noeuds, modifier leur contenu, modifier leur style, modifier leurs attributs, etc.

Accéder aux noeuds

Dans le chapitre précédent j'ai déjà évoqué les méthodes querySelector et querySelectorAll de l'objet document.

En JavaScript la page web est l'objet document qui est un sous objet de l'objet window (fenêtre du navigateur).
Donc pour accéder à un noeud de la page je vous conseille d'utiliser ces nouvelles méthodes de l'API selectors (qui fait partie du pack HTML5).

La méthode querySelector()

Donc l'argument de la méthode querySelector est un sélecteur CSS !

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.

Les anciennes méthodes

Il y a naturellement la méthode historique d'accès : getElementById (DOM-2)
Mais elle présente un gros inconvénient : oblige (dans le code HTML) à attribuer à chaque noeud susceptible d'être manipulé en JS un ID.

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éfinies dans la balise début du noeud donc via l'attribut style.
Avec cette syntaxe il n'est donc pas possible de récupérer une propriété du noeud 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.

Modifier les valeurs des attributs d'un noeud

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 noeuds</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 texteon 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 de style

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 noeud

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 noeuds.
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 script serait beaucoup plus simple en utilisant le gestionnaire d'événements de HTML c'est à dire en appelant les fonctions dans le HTML et non pas dans le script. Par exemple : button onclick = "fdemasquer()".
Mais surtout en utilisant Jquery ...

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 left doivent une chaîne 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 chaîne ("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ébarasser 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.

Non utilisation du 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ésente 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