Accueil

Traduction

Tutoriel sur Javascript

Recherche dans ce tuto

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

JavaScript : quelques routines intéressantes

L'emploi d'un script dans une page web peut apporter une plus-value à celle-ci.

Générer du HTML via JS

Un script de quelques lignes mais comportant des boucles peut produire beaucoup de code HTML.
Je n'en dit pas plus dans cet article car je consacre tout un chapitre sur ce sujet.
Générer du HTML via un script

Génération aléatoire d'un entier

Génération d'un entier compris entre 0 et 9

À saisir dans la console :

> flottant = Math.random()
0.7108295069183412
> entier = Math.floor(flottant*10)
7

Il faut utiliser la méthode floor() afin que l'entier généré soit compris entre 0 et 9.
En effet floor() arrondit à l'entier inférieur.

Simulation d'un lancer de dé

L'entier généré de façon aléatoire doit être compris entre 1 et 6.

flottant = Math.random()
0.5338635313763636
de = Math.ceil(flottant * 6)
4

Il faut utiliser la méthode ceil() afin que l'entier généré soit compris entre 1 et 6.
En effet ceil() arrondit à l'entier supérieur.

Effacer l'élément courant

Le code de la page (extrait)

Dans l'exemple le code JS est mélangé avec le code HTML. On dit que l'on utilise le gestionnaire d'événements HTML.
Ici cette solution est acceptable car il n'est associé à chaque élément qu'une instruction JS.

Comme l'effacement porte sur l'élément HTML courant (celui qui porte l'évènement) on peut employer le mot clé "this".

Pour effacer les différentes images j'ai utilisé 4 techniques différentes.
La différence majeure entre display:none et visibility:hidden est que le premier supprime entièrement un élément de la mise en page tandis que le second cache l'élément mais réserve la place qu'il occupait.
width=0 a le même effet que display:none tandis que opacity =0 a le même impact que visibility : hidden.

Le rendu

Supprimez les images de la première rangée ; notez que le trait (balise HR) remonte.
Effacer les images de la deuxième rangée ; remarquez que le trait ne bouge pas ...

Afficher l'heure et la date courantes

Le code HTML de la page

Le script

// instant présent
var aujourdhui = new Date(); 
// affichage date courante
var annee = aujourdhui.getFullYear(); 
var mois =aujourdhui.getMonth()+1; 
var jour = aujourdhui.getDate();
var joursemaine = aujourdhui.getDay() ;
var tab_jour = 
	new Array("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi");
document.write('Nous sommes le : ' +  tab_jour[joursemaine] 
+ ' ' + jour + '/' + mois + '/' + annee) ; 
	
// affichage de l'heure courante avec actualisation chaque seconde
var boite = document.querySelector('#heure'); // référence le DIV dans le script
function horloge() 
{
	var heure =new Date();
	boite.textContent =
		"il est : " + heure.getHours()+" heures "+ heure.getMinutes() 
			+" minutes  "+ heure.getSeconds() + " secondes"
}
var fin = setInterval("horloge()", 1000); 
// la méthode setInterval() retourne un entier qui ici est récupéré dans la variable "fin". 

Le constructeur new Date() sans argument retourne l'instant présent.

J'ai utilisé un tableau pour associer à 0 la chaine "dimanche", à 1 la chaine "lundi", etc.

La fonction "horloge()" est appelée toutes les 1000 millisecondes (chaque seconde) via à la méthode setInterval(). La fonction "horloge" affiche l'instant présent dans une boite DIV.

Observez bien l'instruction var fin = setInterval("horloge()", 1000).
Ce qui signifie que la fonction "horloge" est appelée chaque seconde (1000 millisecondes).

Le rendu de ce code :

Réaliser un "slideshow" ou diaporama

Dans un document HTML les images apparaissent sous forme de miniatures. Mais si clic sur une des miniatures, celle-ci est alors affichée en grand dans une boite avec la légende appropriée.

Le code CSS correspondant

.miniature {display : inline-block ; height : 80px ; 
	width : auto ; margin : 5px; }
div {width : 500px ; height : 400px ; 
	margin : 10px auto;  
	background-image: url(../images/palourde_royale.jpg); 
	background-size : contain; 
	background-color : white; 
	background-repeat : no-repeat;}

Notez l'emploi de la propriété background-size : contain. Ainsi l'image agrandie dans le DIV n'est jamais déformée.
Pour en savoir davantage sur les propriétés CSS préfixées "background"

Le code de la partie BODY

Pour chaque image appel de la fonction "afficher" argumentée avec "this" ; mot clé qui désigne donc ici l'image objet du clic.

Le script

var boite = document.querySelector('div'); 
var legende =document.querySelector('h4');
		
function afficher(image) 
{ 
	var source = image.getAttribute('src'); 
	// on récupère le chemin de l'image sélectionnée
	source = "url(" + source +")" ;
	// on produit la valeur de la propriété background-image
	boite.style.backgroundImage =source ; 
	// on modifie la propriété backgroundImage de boite
	// on récupère la valeur de l'attribut alt de l'image sélectionnée
	var texte = image.getAttribute('alt');
	// on change la légende 
	legende.textContent = texte; 
}

Notez l'emploi de la méthode élément.getAttribute() pour modifier les attributs de l'élément DIV.
Attention en JS les propriétés CSS sont écrites selon la notation "camelCase" : suppression du tiret qui est remplacé par une lettre majuscule. Ainsi "background-image" devient "backgrounImage" dans le script.

Le rendu dans un Iframe

Adaptation du code

Si vous voulez un diaporama avec 6 images, il suffit d'ajouter un élément IMG dans le code HTML.
Il n'y a rien à changer dans le script !

L'utilisation du gestionnaire d'évéments HTML j'avère ici un choix judicieux.
Si j'avais utilisé le gestionnaire d'évènéments JS, l'ajout d'une nouvelle miniature obligerait à modifier à chaque fois le script.

Réaliser un menu "hamburger"

Ces trois barres horizontales sont omniprésentes : jeux vidéo, applications, sites internet, logiciel, etc.
Lorsque vous les voyez, vous savez immédiatement qu’il s’agit d’un bouton vous donnant accès à un menu.
Sur un petit écran les pixels sont précieux. le menu "hamburger" est une bonne solution pour ce type de terminal.

Donc par défaut un menu est masqué.
Si clic sur l'icône 'hamburger' alors le menu s'affiche.
Nouveau clic sur l'image 'hamburger' et alors le menu disparait.

Le code de la page

Partie HTML

J'ai récupéré l'image 'hamburger_icone.png' sur la 'toile'. Il suffit de taper 'hamburger' dans le moteur de recherche puis de cliquer sur l'onglet 'images'. J'ai ensuite redimensionné l'image avec PAINT car elle était trop lourde.

Notez dans la balise IMG l'attribut "onclick" qui permet d'appeler une fonction JS.
L'icône est stylé via la classe 'hamburger' : voir feuille de style ci-dessous.

Le script

Il consiste en une fonction nommée "mafonction".

function mafonction() 
{
  var menu = document.querySelector('nav');
  if (menu.style.display == "block") {menu.style.display = "none";  } 
  else {menu.style.display = "block";  }
}

La variable "menu" référence l'élément NAV.
Si la boite NAV est affichée alors la fonction masque NAV.
Si la boite NAV est masquée alors la fonction affiche NAV.

Partie CSS

/* Styler le menu de navigation */
nav {background : navy ;  position: relative; z-index :1;}

/* Masquer le menu de navigation */
nav {display: none;}

/* Styler les liens du menu */
nav a {display: block; color: skyblue;  height : 50px ; 
	line-height : 50px ; padding-left : 40px;  
	text-decoration: none;  font-size: 15px; }

/* Styler l'icone hamburger */
img.hamburger {position : absolute ; right: 0px ; 
	top : 0px; width : 40px; z-index : 9; }

/* Styler le lien survolé */
nav a:hover { background-color: skyblue ;  color: navy; }

La boite NAV a une valeur pour la propriété z-index inférieure à celle affectée à l'icône ; ainsi ce dernier est toujours en premier plan.
Par défaut la boite NAV est masquée.
Les liens contenus dans NAV sont de type block, couleur de la légende : skyblue, pas de soulignement, etc.
L'cône est positionné en haut à droite de l'écran.
Sur survol d'un lien les couleurs de fonds et de texte sont inversées.

Test du code

Vous cliquerez sur le "hamburger" pour afficher le menu.
Vous cliquerez de nouveau sur cet icône pour le masquer.

Déplier un texte

Thématique : dans une page une partie de l'article rédactionnel est masqué par défaut, seul le premier paragraphe ("l'accroche") apparait. Mais en cliquant sur un lien la partie masquée s'affiche !

Le code HTML

La boite DIV est identifiée "suite".

Si clic sur la balise A provoque l'exécution de la fonction JS "afficher_suite".

Le code CSS

div#suite{display : none; }

Par défaut la boite identifiée "suite" est masquée.

Le script

function afficherSuite()
{
	document.getElementById('suite').style.display ='block';
}

La fonction démasque l'élément identifié "suite".

Le rendu de ce document HTML

Solution alternative

La solution HTML5 consiste à utiliser les éléments details & summary.
Autres nouvelles balises HTML5

Effet fondu avec le framework "vanilla"

Vanilla.js, c'est quoi ?

Quand vous télécharger ce framework vous récupérez un fichier qui pèse 0 octet ... Donc Vanilla.js c'est une plaisanterie ; utiliser "vanilla" signifie en fait : programmer en JS natif (sans une couche de framework).
Les auteurs de cette blague reprochent aux frameworks JS, tel jQuery, un ralentissement certain dans l'interprétation du code.
Il font à juste titre remarquer que le JS natif a tellement progressé que le recours aux frameworks JS est souvent inutile.

Effet fondu en JS natif

Un “fondu” est une disparition OU apparition progressive d’un élément. C’est donc un effet de transition d’un état à un autre.
jQuery propose des méthodes pour faire apparaitre / disparaitre progressivement une collection d'éléments : fadeIn,() fadeOut(), fadeToggle().

Mais si vous combinez des nouveautés proposées par la dernière version de JavaScript et celles proposées par CSS version 3, vous pouvez éviter l'emploi de jQuery.

Le code de la page (extrait

CSS : je définis une classe "effacer" qui définit une transition sur deux propriétés : width & opacity.
Script : je parcours toute les images de la page et à chaque image j'applique la commande toggle de classList.

Le rendu

Simuler le tirage de 5 cartes dans un jeu de 32 cartes

Le code de la page

Le code est très succinct grâce à l'emploi d'une nouvelle structure de données proposée par la version 6 de JS : les ensembles ou "sets".

Pour en savoir plus sur les objets set : les tableaux JS : objets array, map & set

Le rendu

Exécution asynchrone grâce à setTimeout()

JavaScript est "monothread" c'est-à-dire que le moteur d'exécution du code ne peut effectuer qu'une seule opération à la fois (contre plusieurs en parallèle s'il était "multithread"). Donc tant qu'une instruction JavaScript n'est pas achevée l'interface Web reste bloquée. On parle de traitement synchrone.
C'est ce qui se produit, par exemple avec une instruction basée sur alert() ; tant que le visiteur n'a pas cliqué sur le OK de la boite de dialogue le script est en attente ...

Un traitement synchrone peut être très pénalisant si le temps d'exécution d'une ligne de code est très longue ...
Pour contourner ce problème il est possible de réaliser un appel différé d'une instruction grâce à la fonction de rappel setTimeout().

Dans les deux exemples qui suivent le traitement est asynchrone.

Premier exemple

Le script

Le rendu

Actualisez la page avant !

Il y a affichage de "Bonjour" puis "Comment allez vous" et seulement 5 secondes plus tard affichage de "Au revoir".
Donc le traitement est asynchrone c'est à dire que les instructions ne sont pas exécutées selon leur ordre physique dans le script.

Exemple 2

Différer l'exécution d'une instruction via setTimeout() peut s'avérer intéressante si celle-ci prend beaucoup de temps.

Selon la loi des grands nombres, la probabilité d'obtenir le chiffre 1 est de 1/6, même chose pour le chiffre 2 ..., le chiffre 6.
1/6 ième donne en pourcentage de 16.67 %.
Vérifions si la génération aléatoire d'entiers en JavaScript donne les résultats attendus par cette loi mathématique.

Le code de la page (extrait)

Vous savez que l'exécution d'une instruction JS "document.write()" efface le contenu précédent de la page.
Aussi le traitement sera ici asynchrone ; l'exécution automatique de la fonction "calcul" sera différée de 5 secondes ; le temps nécessaire pour que le visiteur lise tranquillement le texte affiché ; texte ensuite effacé par l'exécution du document.write suivant.

Analyse du script

Le rendu

Actualisez la page pour relancer le script !

On est pas loin de 1/6 pour chaque face.
Modifiez le code pour simuler 50 000 lancers et observez ...

La méthode window.requestAnimationFrame()

Pour appeler régulièrement et automatiquement une fonction vous pensez à setInterval().
Sachez qu'il existe désormais une nouvelle méthode : window.requestAnimationFrame().

Reprenons la thématique "affichage de l'instant présent"

Nous pouvons désormais écrire le script de la façon suivante (extrait):

...
var boite = document.querySelector('#heure'); 
// référence un DIV identifié "heure"
function horloge() 
{
	var heure =new Date();
	var heureGMT = heure.toGMTString();
	boite.textContent = "Il est : " + heure.getHours()+" heures "+ 
		heure.getMinutes() +" minutes  "+ heure.getSeconds() + " secondes";
	window.requestAnimationFrame(horloge);
}
window.requestAnimationFrame(horloge);

Attention, il faut deux instructions window.requestAnimationFrame(horloge); : une en dehors de la fonction (premier appel) et une autre dans la fonction (récursivité).
Cette nouvelle méthode appelle la fonction 60 fois par seconde donc rend les animations très fluides.

Le rendu

Gestion des erreurs de saisie

Il ne faut pas qu'un script "plante" à la moindre erreur de saisie de l'utilisateur.
JavaScript propose désormais une structure très interéssante : try ... catch ...
Je n'en dis pas plus. Je vous invite à suivre le lien ci-dessous :
la gestion des exceptions

Empécher le plagiat de votre site ?

Vous avez sans doute remarqué en surfant sur Internet que de plus en plus de sites Web bloquent le clic droit, interdisent la sélection du texte et des images ainsi que la fonction copier du clavier "Ctrl + C".
Ceci afin d'éviter le plagiat du contenu du site.

Première solution

Ci-dessous une document HTML dans lequel il est impossible de sélectionner du texte pour le copier.
Par contre on peut encore "enregistrer l'image sous ... " car le clic droit est toujours possible ; donc on peut afficher le menu contextuel. Donc le texte est protégé mais pas les images ...

Le code correspondant

Le dispositif anti copier-coller du contenu est effectué uniquement en CSS.

Notez la règle de style : body {user-select : none; }

Pour protéger du plagiat les images on peut complèter par du JS.

Deuxième solution : JS à l'ancienne

Ci-dessous une document HTML dans lequel le copier-coller du texte est rendu impossible et on ne peut plus via un clic droit, accéder au menu contextuel donc "enregistrer l'image sous ... ".
Donc on protège du plagiat non seulement le texte mais aussi les images.

Le clic droit sur un élément n'a aucun impact : le menu contextuel n'apparait pas.
Il est impossible de sélectionner du texte.
Il est impossible de produire un CTRL + C

Le code correspondant (extraits)

Observez bien le code de la balise BODY.
J'utilise le gestionnaire d'événements HTML pour interdire le comportement par défaut du navigateur sur trois événements..

Analyse de ces trois instructions JS

oncontextmenu='return false' : en cas de clic droit, le menu contextuel n'apparait plus.

onkeydown='return false' : ce code interdit le raccourci CTRL + C.

onmousedown='return false' : ce code interdit la sélection de texte par la souris donc le "coller" qui suit.

Troisième solution : JS moderne

L'utilisation du gestionnaire d'événements HTML est une technique obsolète ; ce mélange de HTML et de JS est génant ; la dispersion du code JS complexifie sa maintenance.
La syntaxe onEvent ="return false" est aussi dépassée ; il faut mieux utiliser la méthode nouvelle (introduite par la version 6 de JavaScript) : événement.preventDefault().

Le code correspondant

Plus de code JS associé à la balise BODY.
Le script se résume à :

var contenu = document.querySelector('body'); 
contenu.oncontextmenu = function(e) {e.preventDefault();}
contenu.onkeydown = function(event) {event.preventDefault();}
contenu.onmousedown =function(evenement) {evenement.preventDefault();}

Trois fonctions anonymes.
Chaque fonction est basée sur la méthode événement.preventDefault().
Nommez l'objet "event" comme vous voulez. L'usage est d'utiliser le terme "e" ou "event" ou pour les francophiles "evenement" ou "evt".

Le rendu

Je n'affiche pas le document HTML car le rendu est strictement identique à la solution précédente (JavaScript à l'ancienne).

Les littéraux de gabarit

La version 6 de EMACScript propose les "template literals" OU "template strings" ; traduisez en français "littéraux de gabarits". Grâce à cet outil on évite les fastidieuses concaténations.
De plus tout 'template string' contenu dans une instruction HTML est convertie en code MathML à condition d'avoir chargé un convertisseur de littéraux de gabarit en code MathML.

Le langage MathML

Attention les accents graves ne sont pas très visibles ; ne confondez pas avec apostrophe !

Un document HTML (extrait du code)

Le code HTML :

Je charge la librairie "ASCIIMathML.js".
Deux élements BR contiennent des "template strings". Ces littéraux de gabarit contiennent des formules mathématiques avec une syntaxe peu satisfaisante.

Le script :

	var nom ="Dupont" ; 
	var prenom ="Marcel"; 
	var age = 50; 
	var identite = "Nom : " + nom + " - prénom : " + prenom + " - âge : " + age ; 
	document.write(identite); 
	var identite2 = `Nom : ${nom} - prénom : ${prenom} - âge : ${age} `;
	document.write('
'); document.write(identite2);

Je vous montre que plutôt que concaténer des variables avec des chaines, il est plus élégant d'englober ces variables dans une chaine délimitée par des ` (accents graves) ; il faut utiliser la notation "moustache" pour les variables.

Le rendu

Deux jolies formules mathématiques apparaissent à la place des deux "template strings" contenues dans les balises BR.

Un jeu de logique en JS

Le code du document

Le code HTML :

Le script :

// Deviner un entier généré de façon aléatoire
function jeu()
{
	var infos = document.getElementById('infos');
	var contenu ="";
	var  flottant = Math.random();
	// entier généré compris entre 1 et 99
	var nombre_secret = Math.floor(flottant*100);
	console.log(nombre_secret); // instruction provisoire
	var i =1 ; 
	var compteur = 0; 
	while (i==1)	// boucle infinie
	{
		var nombre_propose = prompt("saisir un entier compris entre 1 et 99"); 
		nombre_propose = parseInt(nombre_propose); 
		compteur++; 
		if (nombre_secret == nombre_propose) 
			{
				contenu = `Vous avez trouvé le nombre secret qui était : ${nombre_secret} `; 
				alert(contenu); 
				break; 
			}

		else if (nombre_propose < nombre_secret) 
			{contenu = "Trop petit ! "; alert(contenu); }
		else 
			{contenu ="Trop grand !"; alert(contenu); }
	} // fin boucle
infos.innerHTML =`Vous avez trouvé en : ${compteur} essais ` ; 
} // fin fonction 

Notez l'utilisation des "template strings".
Remarquez la boucle infini (la condition est toujours vrai).

Le rendu

Le rendu dans un nouvel onglet afin que vous puissiez afficher la console du navigateur. Cliquez ici !

API "drag & drop"

L'interface HTML "Drag and Drop" (pour glisser-déposer) permet à des applications d'utiliser des fonctionnalités de glisser-déposer dans le navigateur.
Cette API fait l'objet de tout un chapitre dans le tuto HTML5

Animer un élément progress via JS

Je veux simuler le téléchargement d'un gros fichier avec affichage de la progression du chargement dans un élément progress

La solution 'buggée'

Le script s'exécute beaucoup trop vite ; en une fraction de seconde.

Le code correspondant

Comment ralentir l'exécution d'un script ?

Le langage Python dispose de la fonction sleep(n secondes) qui peut être intégrée dans une boucle pour augmenter le temps d'exécution d'un programme.
JavaScript ne propose pas de fonction équivalente mais il existe les minuteurs : fonctions setInterval() & setTimeout().

La bonne solution

La durée d'exécution est cette fois de 20 secondes !

Le code correspondant

var stop = setInterval(increment,200) : la fonction increment est appelée tous les 200 millisecondes. Cette fonction est appelée 100 fois (l'appel est ensuite bloqué par clearInterval()).
Donc 100 par 200 millisecondes = 20 secondes.
L'instruction console.log(compteur) est provisoire.

Astuces relatives aux formulaires

Vous verrez à cette occasion une utilisation étonnante des classes CSS ...

Toutes ces astuces sur JS et les formulaires sont traitées dans un autre chapitre du même tuto.
Je vous invite à suivre le lien suivant : JS et les formulaires