Tutoriel JavaScript côté client
JavaScript | |
---|---|
◀▬▬▶ | |
⚐ à améliorer | ☸ intermédiaire |
⚒ 2016/09/26 | |
Prérequis | |
Voir aussi | |
Introduction
Ce tutoriel présente un survol des caractéristiques principales du langage JavaScript relatives à l’utilisation « côté client », c’est-à-dire l’utilisation la plus fréquente dans des pages/applications web à l’intérieur d’un navigateur. JavaScript permet en effet de "dynamiser" le contenu d'une page une fois qu'elle est disponible dans le navigateur. Pour suivre les exemples disponibles dans ce tutoriel il faut une connaissance de base du langage. Voir :
Rappel de la structure d'une page web
Avant d'aborder JavaScript, il est utile de rappeler la structure d'une page web qui se compose généralement de trois éléments :
- HTML : cet élément détermine le contenu de la page, c'est-à-dire les éléments textuels et graphiques affichés à l'écran.
- CSS : cet élément détermine la mise en page, la manière de présenter les éléments HTML de la page. On peut déterminer à travers les CSS les couleurs, les polices, l'alignement des éléments, etc.
- JavaScript : cet élément ajoute des interactions et des comportements (ou effets) au contenu et/ou à la présentation de la page. Ces phénomènes permettent de modifier la page sans la nécessité d'actualiser le navigateur.
Outils pour le développement et le débug
Pour développer en JavaScript seulement deux outils sont strictement nécessaires :
- Un navigateur web
- Un éditeur de texte
Des outils supplémentaires peuvent sans doute rendre le développement plus aisé. Voici une liste de ces outils :
- Brackets : éditeur de texte open-source qui supporte la syntaxe et le complètement automatique du code JavaScript
- Google Chrome Developer Tools : outils de développement et débug du navigateur Google Chrome
- Firefox Developer Tools : outils de développement et débug du navigateur Firefox
Console JavaScript
La Console JavaScript (déjà introduite dans le Tutoriel JavaScript de base devient encore plus utile pour JavaScript côté client, car elle peut non seulement interprèter le langage de base, mais également avoir accès aux éléments de l'environnement, c'est-à-dire le navigateur web. Nous rappelons que vous pouvez accèder à la Console des manières suivantes :
- Internet Explorer : Menu Outils > Outils de développement (ou F12)
- Firefox : F12 ou plus de fonctionnalités via l'extension Firebug
- Chrome : Menu Outils > Console JavaScript (ou F12)
- Safari : D'abord dans les Options > Avancées cocher la case "Afficher le menu Développement dans la barre des menus", puis CTRL+Alt+I ou CTRL+Alt+C
- Opera: Menu Page > Outils de Développement > Opera Dragonfly (ou CTRL+Shift+I)
Par exemple si vous tapez window.navigator
vous pouvez voir quelques informations sur votre navigateur.
L'avantage de JavaScript côté client consiste dans le fait que les navigateurs web disposent d'un "moteur" qui permet d'évaluer le code JavaScript et l'exécuter à chaque fois qu'une page web, contenant du code JavaScript, est téléchargée par l'utilisateur. Ceci est possible surtout grâce à deux phénomènes :
- L'inclusion de code JavaScript dans des pages HTML ;
- L'existance d'un objet global
window
qui permet à JavaScript d'interagir avec le navigateur web.
Utilisation du code JavaScript dans une page HTML
Il existe trois manières différentes d'utiliser du code JavaScript dans une page web :
- Fichier externe : le code est écrit dans un fichier avec extension .js
- Code "inline" : le code est écrit directement dans la page web elle-même
- Attribut des balises (déconseillé) : le code est écrit dans des attributs des balises des éléments HTML de la page. Cette option, utilisée souvent dans le passé, est aujourd'hui fortement déconseillée et ne sera par conséquent pas illustrée dans ce tutoriel.
Les trois différents manières peuvent être utilisées en même temps, il n'est pas nécessaire de choisir une seule manière pour toute la page.
Fichier externe
Dans le cas d'utilisation d'un fichier externe, ce fichier doit être créé avec l'extension .js. Dans ce fichier doit être contenu seulement du code JavaScript. Le code peut commencer dès la première ligne, il ne faut pas déclarer le type de fichier par exemple à travers une balise XML.
Pour rendre disponible le code d'un fichier externe dans une page web, il faut inclure le lien au fichier dans la balise HTML script
et plus précisément à travers l'attribut src. Voici un exemple :
<script src="path/to/file.js"></script>
Veuillez noter que la balise script
doit être fermée avec la balise de clôture même si à l'intérieur d'une balise avec attribut src il ne faut pas insérer de code.
Le lien au fichier .js suit les règles des liens hypertextuels à tout autre type de fichier, y compris les liens à d'autres pages HTML. On peut donc avoir un lien :
- Absolu : le lien présente tout le URL du fichier, e.g.
<script src="http://tecfa.unige.ch/path/to/file.js"></script>
(ce fichier n'existe pas) - Relatif à la racine du domaine, e.g.
<script src="/path/to/file.js"></script>
. Veuillez noter le / initial qui détermine la racine du domaine. Dans ce cas la page HTML et le fichier JS doivent se trouver sur le même domaine (e.g. http://tecfa.unige.ch) - Relatif à la position du fichier HTML, e.g.
<script src="../path/to/js"></source>
.
Les positionnement relatifs utilisent une notation qui combine un .
ou deux ..
points et un slash /
:
<script src="./file.js"></script>
: le fichier file.js se trouve dans la même position (i.e. même dossier sur le serveur) de la page HTML<script src="file.js"></script>
: le fichier file.js se trouve dans la même position de la page HTML (équivalent à l'exemple précédent et utilisé plus souvent)<script src="assets/js/file.js"></script>
: le fichier file.js se trouve dans un dossier "js" qui se trouve à l'intérieur du dossier "assets". Le dossier "assets" se trouve à l'intérieur du dossier qui contient également la page HTML.<script src="../file.js"></script>
: le fichier file.js se trouve directement dans le dossier "parent" du dossier qui contient la page HTML<script src="../js/file.js"></script>
: le fichier file.js se trouve à l'intérieur du dossier "js" qui se trouve dans le dossier "parent"<script src="../../../../file.js">
: le fichier file.js se trouve 4 niveaux "plus haut" par rapport à la page HTML
Le mauvais pointage d'un fichier externe est une des raisons les plus communes d'erreur dans le code JavaScript et il est par conséquent la première chose à contrôler en cas de problèmes. Une manière simple pour tester les liens à des fichiers JavaScript externes consiste à utiliser le code source de la page HTML. Certains navigateurs permettent en effet de cliquer sur le lien et ouvrir directement le fichier JS. La vidéo suivante montre cette opération en Google Chrome : si le lien est mauvais, le fichier ne sera pas trouvé.
On utilise des fichiers externes souvent dans ces conditions :
- Le même fichier contient du code qui est utilisé dans plusieurs pages d'un site, ce qui évite la nécessité de l'écrire dans toutes les pages. De cette manière, une modification au code doit se faire à un seul endroit et sera automatiquement activé dans toutes les pages.
- Le fichier est une bibliothèque JavaScript
- Il y a beaucoup de code écrit
Code inline
Une autre manière très utilisée d'inclure du code JavaScript consiste à écrire le code directement à l'intérieur de la balise script
. Dans ce cas, il ne faut pas déclarer l'attribut src. Voici un exemple :
<script> alert("Bonjour!"); </script>
La balise script peut contenir seulement du code JavaScript. Dans des exemples sur le web vous pouvez parfois trouver des balises script qui présentent aussi l'attribut type="text/javascript". Cet attribut n'est plus nécessaire car tout navigateur considère automatiquement le code inséré dans cette balise comme du code JavaScript.
On utilise du code "inline" souvent dans ces conditions :
- Le code doit être exécuté précisément à cet endroit dans la page, par exemple parce qu'il ajoute du texte ou des éléments HTML.
- Le code est spécifique à une seule page du site est le code n'a pas de probabilité d'être utilisé ailleurs
- Le code est court
- Le code sert à configurer des options ou initialiser une bibliothèque JavaScript
Placement de la balise script
Un élément auquel il faut faire attention est le placement de la balise script
dans la page HTML. Il faut en effet tenir compte de la manière progressive de charger une page des navigateurs web. Avec le code JavaScript on fait souvent référence à des éléments du DOM (i.e. des balises HTML contenu dans la page). Il faut faire attention dans ces cas à ce que cet élément soit déjà disponible au navigateur.
Même s'il existe des fonctions pour déclencher le code seulement une fois que toute la structure du DOM a été chargée dans le navigateur (e.g. window.onload), il est une bonne pratique d'inclure les balises script
juste avant la balise de clôture /body
:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
</head>
<body>
<script>
//Mon code est mieux ici!
</script>
</body>
</html>
L'objet global window
JavaScript dépend de l'environnement dans lequel il est exécuté à la fois pour afficher les résultats des expressions (i.e. le output), mais également pour recevoir des inputs. Dans JavaScript côté client, cet environnement est représenté par le navigateur web et la communication I/O entre JavaScript et le navigateur web se fait à travers l'objet global window
. Plus concrètement cela signifie que :
- JavaScript peut "intercepter" tous les événements qui concerne la fenêtre du navigateur comme par exemple les clicks, les changements d'URL, le scolling de la page, etc.
- JavaScript peut modifier certaines propriétés de la fenêtre, y compris son contenu.
Les deux opérations sont possible justement grâce à l'objet global window
qui, en tant qu'objet, possède des propriétés et des méthodes. Comprendre l'objet global window peut contribuer à bien comprendre le langage, car en réalité tout le code source écrit en JavaScript côté client peut se diviser en deux catégories :
- Le code qui exploite les propriétés et méthodes mises à disposition par l'objet window
- Le code qui ajoute des propriétés (e.g. des variables) ou des méthodes (e.g. des fonctions) à l'objet global window
En effet, lorsqu'on déclare une variable, ce que nous sommes en train de faire c'est tout simplement d'ajouter une propriété avec le nom de la variable à l'objet global window. En d'autres termes, les deux lignes de code suivantes sont équivalentes :
var cours = "STIC I"
;window.cours = "STIC I"
;
Vous pouvez tester l'équivalence de cette manière :
var cours = "STIC I"; cours === window.cours; //--> true
Le même principe s'applique aux fonctions :
function addTwo(x) { return x + 2; } addTwo(4); // -> 6 window.addTwo(7); //--> 9
En raison du fait que tout appartient à l'objet window, on peut éviter de le déclarer à chaque fois. Le même principe s'applique aux propriétés et méthodes déjà disponibles. Par example la fonction alert()
- qui affiche une boîte popup avec un message - est en réalité la méthode window.alert()
.
À travers JavaScript il est également disponible de changer des propriétés de l'objet global window, ce qui se traduit par des comportements spécifiquent qui modifient le contenu du navigateur. Vous pouvez par exemple essayer d'écrire ce code dans la Console de votre navigateur web pour être rédirigés à la page d'accueil de ce wiki :
window.location = "http://edutechwiki.unige.ch/fr/Accueil";
Pour une liste exhaustive voir la référence de l'objet Window sur W3Schools.
L'objet document
L'objet global window contient plusieurs "sous-objets" dont un des plus importants est le window.document qui peut être directement abrégé en document dans le code. Cet objet représente le contenu de la page HTML, c'est-à-dire l'arborescence des balises qui déterminent le contenu de la page. Si vous insérez la commande document
dans la Console de votre navigateur, vous obtenez en effet le code HTML de la page (le code de l'homepage EduTechWiki à l'image). On se réfère à cette structure avec le nom de Document Object Model, ou DOM.
Vous pouvez par la suite essayer document.head
et document.body
pour obtenir respectivement le contenu de la balise head
(informations) et de la balise body
(contenu) d'une page. Pour sélectionner ensuite des éléments plus spécifiques (e.g. un titre, une image, etc.), il faudra utiliser d'autres méthodes.
- Voir "JavaScript et le DOM" plus bas dans la page
Les boîtes popup de JavaScript
Pour le débutant, créer quelques simples boîtes popup permet se lancer dans la programmation. On peut par exemple poser une question à un utilisateur et ensuite la traiter, puis afficher le résultat.
JavaScript possède 3 sortes de boîtes popup :
- Les boîtes d'alerte
- Les boîtes de confirmation
- Les fenêtres de saisies.
Veuillez noter que ces éléments sont mis à disposition par le navigateur web et donc le contrôle que le développeur a sur leur fonctionnement et leur apparence est très limité. De plus, ils ne sont pas très pratique au niveau expérience utilisateur. En d'autres termes, il s'agit d'outils pratiques pour explorer quelques événements/intérations de base, mais par la suite il faudrait les remplacer par des éléments HTML "customizés" (e.g. des fenêtres modales, des formulaires, etc.).
Les boites d'alerte
Elles affichent un message. Elles servent généralement à donner un message à un utilisateur ou à indiquer un message d'erreur. La boîte d'alerte contient un bouton 'Ok'.
Il faut utiliser la fonction alert()
, c'est-à-dire l'équivalent de window.alert()
alert("Je suis une alerte!");
Vous pouvez structurer votre message dans vos boites en insérant '\n' à la fin de vos phrases. Cela permettra de revenir à la ligne.
alert ('Première ligne.\n Deuxième ligne.');
La fonction alert() ne "return" aucune valeur (i.e. undefined).
Les boîtes de confirmation
La fonction confirm()
affiche une boite de confirmation. L'utilisateur a alors le choix entre 'OK' et 'annuler'.
La fonction accepte un paramètre qui détermine le message affiché à l'intérieur de la boîte :
confirm("Voulez-vous continuer?");
La fonction confirm() "return":
- true si l'utilisateur clique sur OK
- false si l'utilisateur clique sur annuler
Vous povez donc l'utiliser pour simuler des structure de contrôle qui dépendent du choix des utilisateurs:
var continuer = confirm("Voulez-vous continuer?");
if(continuer) {
console.log("L'utilisateur voudrait continuer...");
} else {
console.log("L'utilisateur ne veut pas continuer...");
}
Les fenêtres de saisie
Il est possible de créer une fenêtre pour saisir une valeur grâce à la fonction prompt()
et peut ensuite confirmer ou annuler son choix. La fonction prend deux paramètres : le premier est l'invitation à saisir du texte et le second propose un texte par défaut :
Le code suivant demande à l'utilisateur de saisir son nom, sans donner une valeur préétablie :
prompt("Identification, veuillez saisir votre prénom", "");
La fonction prompt() "return" :
- La valeur saisie lorsque l'utilisateur clique sur OK.
- false si la personne ne saisie aucune valeur ou si elle clique sur annuler
Vous pouvez combiner prompt() et alert() pour créer une première interaction dans une page web :
var name = prompt("Veuillez saisir votre nom", ""); alert("Bonjour " + name);
Attention : la valeur est toujours de type string même si la valeur saisie est un nombre! Essayez cette exemple :
var num = prompt("Choisissez un nombre à ajouter à 100 pour voir le résultat", ""); console.log(100 + num);
Vous pouvez notez que le résultat ne sera pas l'addition souhaitée. Si vous avez choisi 20, le message affiché sera 10020 parce que le 20 saisi est traité comme une suite de caractères et par conséquent le + est interprété comme une concatenation. Pour éviter ce problème, il faut spécifier que le résultat du prompt doit être traité comme un nombre, en utilisant par exemple la fonction parseInt()
:
var num = prompt("Choisissez un nombre à ajouter à 100 pour voir le résultat", ""); console.log(100 + parseInt(num)); //On peut utiliser également Number(num)
JavaScript et le DOM
Une des fonctionnalités les plus utilisées de JavaScript est sa capacité d'interagir dynamiquement avec le DOM, c'est-à-dire la structure hiérarchique des balises HTML d'une page web. C'est notamment grâce à la manipulation des éléments du DOM qui s'avèrent les interactions sur une page : apparition d'un message, déplacement d'un élément d'un endroit à un autre, changement de la taille d'une police, du couleur, etc.
Ces manipulations sont possibles grâce à l'objet document qui fait partie de l'objet global window (voir plus haut dans la page). Les propriétés, méthodes ainsi que les événements associés à cet objet permettent de manipuler le DOM.
- Voir les références de l'objet Document
- Voir le chapitre 13 de Haverbeke (2015)
NB: La manipulation du DOM à travers JavaScript "de base" est un bon point de départ pour comprendre la logique, mais pour des applications plus poussées les développeurs utilisent souvent des bibliothèques JavaScript qui simplifient la syntaxe ou facilitent certains opérations. La bibliothèque la plus utilisée dans ce sens est jQuery.
Manipulation des éléments du DOM
Trouver un élément
Trois méthodes permettent d'identifier un élément dans la structure du document :
document.getElementById(id élément)
: identifie un élément avec l'attribut id="..."document.getElementsByTagName(nom de la balise)
: identifie un ou plusiuers éléments avec le nom de la balisedocument.getElementsByClassName(une ou plusieurs classes)
: identifie un ou plusieurs éléments avec l'attribut class="..."
Une méthode plus générale est également disponible à travers les méthodes :
document.querySelector(css selector)
: cette méthode accepte des identifiants plus complexes par rapports aux trois méthodes vues plus haut dans la page, mais elle est également plus flexible, car elle permet d'identifier les éléments grâce aux selecteurs CSS. Cette méthode identifie le premier élément qui respecte les critères du selecteur.document.querySelectorAll(css selector)
: même principe, mais le résultat est un array avec tous les éléments qui respectent le critère de sélection.
Voici quelques exemples d'utilisation :
document.querySelectorAll("a.external") //cette méthode identifie toutes les balises a avec classe "external" document.querySelectorAll("h1,h2,h3") //cette méthode identifie toutes les titres de h1 à h3
Cette fonction peut également être utilisée à l'intérieur des éléments du DOM :
var monParagraphe = document.getElementById("monTexte"); monTexte.querySelector("span").innerHTML = "Nouveau texte du span";
Ce code identifie le premier élément span à l'intérieur du paragraphe avec id="monTexte" et change le contenu textuel.
Modifier un élément
On peut modifier différentes caractéristiques d'un élément une fois identifié dans le document par exemple à travers ces propriétés :
element.innerHTML
: modifie le contenu de la balise de l'élément sélectionnéelement.attribute
: modifie un attribut de la balise de l'élément sélectionnéelement.style.property
: modifie les propriétés de style de la balise de l'élément sélectionné
Ajouter ou supprimer un élément
On peut ajouter des éléments qui n'existent pas dans la structure de base du document ou supprimer des éléments existants. Exemples :
document.createElement()
: définit un nouvel élément HTMLdocument.appendChild()
: ajoute un élément à la structure existantedocument.replaceChild()
: remplace un élément existant par un autre élémentdocument.removeChild()
: supprime un élément présent dans la structure
Exemples de manipulation
Dans la page suivante, 4 types de manipulation ont lieu lorsque la page a été chargée. Vous pouvez comparer le contenu attendu s'il n'y avait pas de manipulation JavaScript (i.e., la source HTML) avec le résultat final qui s'affiche à l'écran de l'utilisateur dans cette image :
Voici le code de la page entière :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript - exemple de manipulation du DOM</title>
</head>
<body>
<h1>JavaScript - exemple de manipulation du DOM</h1>
<p id="p1">Premier paragraphe.</p>
<p id="p2">Deuxième paragraphe.</p>
<p id="p3">Troisième paragraphe</p>
<p>Quatrième paragraphe.</p>
<div>Je ne suis pas un paragraphe!</div>
<script>
window.onload = function() {
//1. On remplace le texte du premier paragraphe
var p1 = document.getElementById("p1");
p1.innerHTML = "Le texte du premier paragraphe a été modifié par JavaScript";
//2. On ajoute une image au deuxième paragraphe
var p2 = document.getElementById("p2");
var image = document.createElement("img");
image.src = "http://lorempixel.com/200/200/abstract";
p2.appendChild(image);
//3. On supprime le troisième paragraphe
var p3 = document.getElementById("p3");
document.getElementsByTagName("body")[0].removeChild(p3);
//4. On change la couleur du texte des paragraphes
var pp = document.getElementsByTagName("p");
for(var i = 0; i < pp.length; i++) {
document.getElementsByTagName("p")[i].style.color = "#F00";
}
}
</script>
</body>
</html>
L'expression window.onload = function () { ....}
associe directement une fonction anonyme (sans nom) qui est exécutée lorsque la page est chargée (voir plus bas la documentation sur les événements du DOM). Les manipulations 1. et 2. ne nécessitent pas d'explication supplémentaire, tandis que la 3. et la 4. sont un peu plus complexes. Il faut considérer en effet que la méthode getElementsByTagName()
renvoie un liste de noeuds. Une liste de noeuds partage plusieurs caractéristiques avec un array, mais n'est pas exactement la même chose. Par exemple il n'est pas possible d'itérer une liste de noeuds avec la fonction forEach()
, mais on peut le faire avec un cycle for()
en utilisant la notation liste[index] comme indiqué dans l'exemple 4. Pour simplicité, nous nous référons par la suite à une liste de noeuds comme un array, mais tenez présente cette distinction (voir cette page sur w3school pour plus de détails).
Dans la manipulation 3. nous avons identifié d'abord la balise body car il s'agit de la balise "parent" du paragraphe 3 qu'on veut supprimer. Bien qu'il n'y ait généralement qu'une seule balise body, la méthode getElementsByTagName()
renvoie néanmoins un Array, dont le premier et seul élément est notre balise body. Vu que les indices des Array commence à 0, pour identifier notre élément body nous devons ajouter l'indice [0] à la méthode getElementsByTagName("body")
.
Le même principe concerne la manipulation 4., dans laquelle la liste contient par contre plusieurs éléments. Pour changer chaque élément, il faut par conséquent un cycle. Nous avons créé ce cycle à l'aide de la fonction for et nous avons déclaré en tant que fonction de sortie la propriété length de l'Array lui-même. Cette propriété identifie le nombre d'éléments présents dans l'Array.
Événements du DOM
Les événements du DOM permettent d'identifier l'interaction de l'utilisateur avec la page, par exemple à travers les mouvements de la souris, les touches du clavier, etc. Les événements concernent également les éléments eux-mêmes, par exemple dans le cas de l'objet window ou encore des éléments d'un formulaire.
- Voir les références des Événements du DOM
- Voir le chapitre 14 de Haverbeke (2015)
L'événement onload
L'événement onload()
est déclenché lorsqu'un élément (ou tout le DOM) a été téléchargé dans le navigateur web.
Une notation qui est très utilisée et qui, par contre, nécessite de déclarer explicitement l'objet window concerne l'événement onload :
window.onload()
: permet de déclencher du code une fois que la page a été chargée dans le navigateur. Il faut être attentif au fait que cette fonction peut être déclarée seulement une fois dans chaque page. Si plusieurs déclarations de window.onload sont disponibles dans la même page, seulement celle déclarée en dernière sera exécutée.
<script> window.onload = alert("Je ne serais pas affiché"); window.onload = alert("Je serais affiché"); </script>
Un autre problématique relative à cette fonction concerne le fait que l'événement onload s'avère lorsque tous les éléments du DOM ont bien été chargés, y compris images, feuilles de style, etc. Ceci peut parfois retarder le déclenchement de l'événement et par conséquent toutes les éléments interactifs associés à cet événements ne seront pas disponibles.
L'événement onload()
peut être également associé à d'autres objets de la page, comme par exemple les images, les iframes, etc.
Une alternative : DOMContentLoaded
Il existe une alternative qui permet de déclencher du code JavaScript lorsque le DOM (et non pas tout le contenu de la fenêtre comme dans le cas de window.onload) a été chargé grâce à l'événement DOMContentLoaded (voir cette page sur MDN pour plus d'info) :
document.addEventListener("DOMContentLoaded", function(event) {
console.log("Le DOM est disponible!");
});
document.addEventListener("DOMContentLoaded", function(event) {
console.log("Je peux utiliser cette méthode une deuxième fois dans le même code!");
});
Veuillez noter que pour utiliser cet événement il faut utiliser le gestionnaire d'événements addEventListener()
. Cette méthode de l'objet DOM se différencie de window.onload() également par le fait qu'on peut déclarer deux (ou plusieurs fois) cet listener dans des endroits différents du code et les deux événements seront considérés, à moins qu'il ne proposent pas des instructions contrastantes.
Événements liés à la souris
Parmi les événements les plus utilisés dans l'interaction avec le DOM, il y a sans doute les événements liés aux mouvements et clicks de la souris. Voici une liste non exhaustive de ces événements :
click
: l'événement est déclenché avec un click de la sourismouseover
: l'événement est déclenché lorsque la souris passe sur l'élémentmouseout
: l'événement est déclenché lorsque la souris quitte un élément
Pour attribuer un événement à un élément il faut procéder ainsi :
- Identifier l'élément dans la structure du DOM (voir plus haut dans cette page)
- Spécifier un événement déclencheur à travers une des notations disponibles (e.g. document.getElementById("mon-bouton").onclick)
- Associer à cet événement un "event handler" (gestionnaire d'événement), c'est-à-dire le processus (normalement une fonction) qui doit se mettre en marche lorsque l'événement se produit
Il y a deux manières d'associer un événement à un élément :
- Utiliser la propriété de l'événement qui se compose de "on" + événement (élément.onclick, élément.onmouseover, élément.onmouseut, ...) à la quelle on associe une fonction :
élément.onévénement = function () { ... }
- Utiliser la méthode addEventListener qui prévoit deux arguments : l'événement et la fonction à exécuter :
élément.addEventListener(événement, fonction)
Le gestionnaire d'événement peut être une fonction déjà définie ou anonyme. Il faut cependant faire attention au fait qu'un gestionnaire d'événement est une référence à la fonction et non pas l'exécution de la fonction elle-même. En d'autres termes, il ne faut pas utiliser les parenthèses dans un gestionnaire d'événement :
function faireQuelqueChose() { //code } mon-element.mon-evenement = faireQuelqueChose; //OK! mon-element.mon-evenement = faireQuelqueChose(); //Faux, de cette manière la fonction sera exécutée de toute manière, sans attendre l'événement déclencheur
Veuillez par contre noter que si l'on associe comme gestionnaire une méthode d'un objet, alors il faudra de toute manière insérer les parenthèses.
var o = {}: o.faireQuelqueChose = function () { //code } mon-element.mon-evenement = o.faireQuelqueChose; //Faux mon-element.mon-evenement = o.faireQuelqueChose(); //OK!
On peut également utiliser une fonction anonyme en tant que gestionnaire d'événement :
mon-element.mon-evenement = function() { console.log("L'événement a eu lieu"); }
Enfin, il est possible d'utiliser la méthodeélément.addEventListener(événement, gestionnaire)
au lieu de la notation élément.événement = gestionnaire. Cette méthode à l'avantage de pouvoir s'ajouter à des gestionnaires d'événements déjà déclarés sur le même élément, sans les annuler (mais ne s'applique pas à window.onload). Voici un exemple pour illustrer ce principe qui se réfère au click sur un bouton :
<button id="myBtn">Button</button>
<script>
var myBtn = document.getElementById("myBtn");
//1. Utilisation de onclick
myBtn.onclick = function () {
console.log("Événement 1");
}
myBtn.onclick = function () {
console.log("Événement 2");
}
//-> Si on click sur myBtn, on verra dans la console seulement "Événement 2";
//2. Utilisation de addEventListener
myBtn.addEventListener("click", function () {
console.log("Événement 1");
});
myBtn.addEventListener("click", function () {
console.log("Événement 2");
});
//-> Si on click sur myBtn, cette fois-ci, on verra dans la console les deux messages.
</script>
Voici un exemple avec des commentaires pour résumer les différentes utilisations des événements liés à la souris :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript - événements liés à la souris</title>
</head>
<body>
<h1>JavaScript - événements liés à la souris</h1>
<button id="mon-bouton">Cliquez ici</button>
<span id="feedback"></span>
<script>
//Déclarer une fonction en cas d'événement click
function showFeedbackClick() {
document.getElementById("feedback").innerHTML = "J'ai cliqué sur le bouton";
}
//Définir une fonction en cas d'événement mouseover
function showFeedbackOver() {
document.getElementById("feedback").innerHTML = "La souris se trouve sur le bouton";
}
//Identifier le bouton dans le DOM
var monBouton = document.getElementById("mon-bouton");
//Trois manières différentes d'associer une fonction à un événement :
//1. Événement click avec association d'une fonction, sans ()!
monBouton.onclick = showFeedbackClick
//2. Événement mouseover avec méthode addEventListener();
monBouton.addEventListener("mouseover", showFeedbackOver);
//3. Événement mouseout avec fonction anonyme
monBouton.onmouseout = function() {
document.getElementById("feedback").innerHTML = "La souris n'est plus sur le bouton";
}
</script>
</body>
</html>
Pour annuler un gestionnaire d'événement, il suffit de le redéclarer null sa propriété d'événement (e.g. mon-élément.mon-événement = null
) ou d'utiliser la méthode élément.removeEventListener()
.
Il existe une méthode plus souple pour définir un gestionnaire d'événement et que l'on retrouve aussi dans ActionScript.
Dans l'exemple suivant,la fonction start()
sera exécutée dès que la page charge, ceci à cause de l'instruction window.onload = start;
qui associe à l'événement "onload" le nom d'une fonction qu'il faut exécuter. Rien de neuf. Par contre, la gestion du 2ème événement se fait différemment.
<!DOCTYPE html>
<html>
<head>
<title>Event handling</title>
<script type = "text/javascript">
window.onload = start;
// var my_para_button ="";
function start() {
// put an event handler on the div box
var my_para_button = document.getElementById("box");
my_para_button = addEventListener("click", modifyText);
}
function modifyText() {
// get the box
var my_content = document.getElementById("content");
my_content.innerHTML = "That was so good";
}
</script>
</head>
<body>
<div id="box">
<p style="background-color:yellow" id="content">CLICK ME !</p>
</div>
</body>
</html>
Source: http://tecfa.unige.ch/guides/js/ex-intro/event-handler.html
La fonction start ajoute un gestionnaire d’événement au bouton my_para_button en utilisant la méthode addEventListener ("click", modifyText);
. Le premier argument, "click", identifie le type d’événement, et le deuxième donne le nom de la fonction.
Prévenir les événements associées aux balises HTML
Certains balises HTML ont des comportements spécifiques associés. Par exemple si on clique sur l'élément a
, le navigateur sera rédirigé sur le lien correspondant à l'attribut href
. JavaScript permet d'intercepter les comportements automatiques des balises et de les prévenir à travers la notation event.preventDefault()
.
Voici un exemple avec deux liens qui pointent à l'home page de ce wiki, mais dont seulement le deuxième marchera "normalement". D'abord le code HTML :
<ul>
<li>Prevented: <a id="prevented" href="http://edutechwiki.unige.ch/fr/Accueil">Go to EduTeckWiki (fr)</a>
</li>
<li>Normal : <a id="not-prevented" href="http://edutechwiki.unige.ch/fr/Accueil">Go to EduTeckWiki (fr)</a>
</li>
</ul>
Et ensuite le code JavaScript qui permet de prévenir le comportement normal du premier lien.
//Prevented
document.getElementById("prevented").onclick = function(event) {
//Prevent the default link behavior
event.preventDefault();
//Change the text of the link
this.innerHTML = "JavaScript intercepted the normal event and prevented it!"
};
On utilise souvent la méthode preventDefault()
pour éviter que la page soit réchargée, notamment avec les formulaires. Ceci permet de garder la session de la page active et donc de maintenir toutes les informations et manipulations de l'utilisateur au niveau client.
L'élément this dans le DOM
Lorsqu'une fonction est associée à un élément du DOM, cet élément est disponible à l'intérieur de la fonction elle-même, à travers l'élément this
. Ceci permet de ne pas avoir à identifier à nouveau l'élément dans le DOM. Voici un exemple qui modifie le label d'un bouton et le désactive après le click :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript - this</title>
</head>
<body>
<h1>JavaScript - this</h1>
<button id="mon-bouton">Cliquez ici</button>
<script>
var monBouton = document.getElementById("mon-bouton");
monBouton.onclick = function() {
//Changer le label du bouton
this.innerHTML = "J'ai cliquez sur le bouton";
//Désactiver le bouton
this.disabled = "disabled";
}
</script>
</body>
</html>
L'élément this
peut être très utile lorsqu'on genère plusieurs éléments dynamiquement pour associer un comportement à chaque élément. Voici un exemple similaire au précédent, mais dans lequel 5 boutons sont créés avec la fonction document.createElement("button")
. Chaque bouton créé est associé ensuite à un événement de type onclick
, à l'intérieur du quel on fait référence au bouton avec l'élément this
:
var list = document.getElementById("btnList");
for(var i = 1; i <= 5; i++) {
var btn = document.createElement("button");
btn.innerText = "Cliquez sur le bouton " + i;
btn.onclick = function () {
this.innerText = "J'ai cliqué sur ce bouton";
this.disabled = "disabled";
}
list.appendChild(btn);
}
Tester cet exemple et l'exemple précédent
JavaScript et CSS
Utiliser les propriétés de style
Javascript permet de modifier non seulement la structure du DOM, mais également les propriétés de style des éléments. Il est possible de modifier toutes les déclarations de style disponibles dans un fichier CSS à travers la notation :
élément.style.propriété = "nouvelle valeur";
Par exemple le code suivant modifie la couleur du texte d'un élément du DOM avec id="mon-paragraphe" :
document.getElementById("mon-paragraphe").style.color = "#F00";
Les changements de style peuvent être appliqués à tout moment dans le code et être donc associés, par exemple, à des événements (voir plus haut dans la page).
En considération du fait que les noms des propriétés des objets en JavaScript ne peuvent pas contenir le caractère -
, toutes les propriétés CSS qui prévoient ce diviseur doivent être modifiées en "camel case", e.g :
- background-color --> élément.style.backgroundColor
- border-color --> élément.style.borderColor
- font-size --> élément.style.fontSize
- ...
En alternative vous pouveez utiliser la notation type array : élément.style["background-color"]
Utiliser les classes
JavaScript permet également de manipuler les noms de class des éléments. La manière la plus simple consiste à utiliser la propriété className
pour lire ou définir la class d'un élément :
//Lire var maClasse = élément.className; // --> e.g. active //Définir élément.className = 'active'
Il faut noter que la propriété className se réfère à tout l'attribut HTML. Voici un élément HTML qui a plusieurs classes :
<div id="myDiv" class="class1 class2 class3">Mon div</div>
Et voici le résultat de quelques opérations avec JavaScript
var myDiv = document.getElementById("myDiv");
var classes = myDiv.className; // --> class1 class2 class3
//Ajouter une quatrième class
myDiv.className = classes + " class4"; // --> class1 class2 class3 class4
//Remplacer avec une seule class
myDiv.className = "class99"; --> class99
Pour faciliter la gestion de plusieurs classes sur le même élément, JavaScript met à disposition la propriété classList
qui possède plusieurs méthodes (voir la liste complète) parmi lesquelles on trouve :
élément.classList.add(class, [class])
: permet d'ajouter une ou plusieurs nouvelles classes à l'élémentélément.classList.remove(class, [class])
: permet d'enlever une ou plusieurs classes à l'élément
Les deux codes suivants sont donc équivalents :
var myDiv = document.getElementById("myDiv");
//Avec className
var classes = myDiv.className; // --> class1 class2 class3
myDiv.className = classes + " class4"; // --> class1 class2 class3 class4
//Avec classList
myDiv.classList.add("class4"); // --> class1 class2 class3 class4
Une autre méthode utile associée à la propriété classList
est la méthode toggle()
qui :
- Ajoute une classe si la classe n'est pas associée à l'élément
- Enlève une classe si la classe est déjà associée à l'élément
JavaScript et les formulaires
Même si JavaScript ne se limite plus simplement à ces tâches, la manipulation des formulaires et des éléments des formulaires (e.g. champs de texte, etc.) restent des fonctionnalités très utilisées (et utiles). JavaScript peut en effet identifier le contenu des éléments des formulaires et utiliser se contenu dans sa logique de programmation pour apporter des changements à la page. Par exemple la propriété .value permet d'identifier le contenu d'un champ de texte :
var maValeur = document.getElementById("mon-champ-de-texte").value;
Cette notation, comme toute propriété, peut être utilisé aussi pour modifier le contenu d'un champ.
window.onload = function() { document.getElementById("mon-champ-de-texte").value = "Nouvelle valeur"; }
Voici un exemple qui utilise l'événement onchange pour suggérer une valeur combiné du prénom et nom d'une personne. Si le champ complet est modifié, néanmoins, aucune suggestion ne sera plus faite.
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript - formulaires</title>
</head>
<body>
<h1>JavaScript - formulaires</h1>
Prénom : <input type="text" id="prenom"><br>
Nom : <input type="text" id="nom"><br>
Nom complet : <input type="text" id="nom-complet"><br>
<script>
var prenom = document.getElementById("prenom");
var nom = document.getElementById("nom");
var nomComplet = document.getElementById("nom-complet");
function suggererNomComplet() {
nomComplet.value = nom.value + ", " + prenom.value;
}
prenom.onchange = suggererNomComplet;
nom.onchange = suggererNomComplet;
nomComplet.onchange = function() {
prenom.onchange = null;
nom.onchange = null;
}
</script>
</body>
</html>
Validation des formulaires
Un autre cadre d'utilisation de JavaScript concerne la validation des formulaires avant qu'ils soient soumis au serveur (i.e. avec actualisation de la page). Cette fonction est désormais partiellement prise en charge par les attributs HTML5 tels que "required", mais il y néanmoins des validations plus complexes qui nécessitent du codage JavaScript.
Dans la structure du DOM, JavaScript peut identifier des champs d'un formulaire de plusieurs manières. On peut utiliser la méthode getElementById() et attribuer à chaque champ un id univoque, ce qui est probablement la manière plus sûre. Sinon, JavaScript identifie les formulaires et ses champs à travers un Array qui se construit sur l'attribut name du formulaire et des champs :
<form name="mon-formulaire">
<input type="texte" name="mon-champ1">
<input type="texte" name="mon-champ2">
</form>
Le form peut être identifié à travers la notation document.forms['mon-formulaire']
tandis que les champs respectivement avec :
document.forms['mon-formulaire']['mon-champ1']
document.forms['mon-formulaire']['mon-champ2']
.
Il est également possible d'utiliser la notation "par point", au lieu des parenthèses carrées, si le nom du champ respecte la notation correcte pour les variables JavaScript.
Voici un simple exemple de validation pour montrer brièvement le mécanisme. Le code JavaScript contrôle que le champ email ne soit pas vide et que les conditions ont été acceptées à travers le checkbox.
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript - formulaires</title>
</head>
<body>
<h1>JavaScript - formulaires</h1>
<form name="mon-formulaire" method="post" action="/submit">
Email : <input name="email" type="text"><br>
<input type="checkbox" name="conditions"> J'accepte les conditions d'inscription<br>
<input type="submit" value="Envoyer">
</form>
<script>
var monForm = document.forms["mon-formulaire"];
var monEmail = monForm["email"];
var mesConditions = monForm["conditions"];
monForm.onsubmit = function() {
if (monEmail.value !== "" && mesConditions.checked) {
//Le formulaire sera soumis au serveur et la page actualisée
return true;
} else {
window.alert("Vous devez insérer votre email et accepter les conditions");
//Le retour false fait ainsi que le formualire ne soit pas soumis et la page pas actualisée
return false;
}
}
</script>
</body>
</html>
- Voir le chapitre 18 de Haverbeke (2015)
Les fonctions liées au temps
Grâce à l'objet global window il est possibile d'exécuter du code en fonction du temps, plus spécifiquement :
setTimeout()
permet d'exécuter du code une seule fois après une période de temps définie ;setInterval()
permet d'exécuter du code à des intervalles reguliers de temps.
setTimeout()
La syntaxe pour utiliser la fonction setTimeout()
est la suivante :
setTimeout(function,milliseconds, [param1,param2,...]);
Les paramètres sont optionnels, tandis que la fonction à exécuter et le temps en milliseconds sont obligatoires. Voici un example qui affiche une boite d'alerte après 5 seconds (i.e. 5 * 1000 milliseconds) :
var afficherApres5seconds = setTimeout(function () { alert("Message"); }, 5000);
Il est également possibile de définir d'abord une fonction et ensuite passer son nom comme référence dans la fonction setTimeout() :
function alertMessage() { alert("Message"); } var afficherApres5seconds = setTimeout(alertMessage, 5000);
Associer la fonction setTimeout() à une variable est utile si on veut annuler l'exécution de la fonction avant que la période de temps définie avec la notation :
clearTimeout(variable);
En ajoutant cette ligne de code à l'exemple précédent, la fonction alertMessage() ne sera pas déclenchée :
function alertMessage() { alert("Message"); } var afficherApres5seconds = setTimeout(alertMessage, 5000); clearTimeout(afficherApres5seconds);
setInterval()
La fonction setInterval
est similaire à setTimeout() si ce n'est pour le fait que le code sera exécutée chaque X milliseconds. La syntaxe reste par contre la même :
setInterval(function,milliseconds, [param1,param2,...]);
Voici un exemple qui affiche dans la console un message chaque 3 seconds (sans arrêt si la page ne sera pas fermée ou après changement de URL) :
var chaque3seconds = setInterval(function () { console.log("...encore 3 seconds"); }, 3000);
Pour limiter les cycles on peut utiliser un conteur et utiliser la fonction clearInterval
direcement à l'intérieur de la fonction invoquée par setInterval() :
var count = 0; var cinqFois = setInterval(function () { count++; console.log(count); if(count == 5) { clearInterval(cinqFois); } }, 2000);
La sécurité avec JavaScript
La sécurité en JavaScript n'est pas à négliger car de nombreux exploits existent et sont assez facilement mis en place. Avant de commencer à lire ce qui suit, il est important de savoir que le JavaScript est interprété du coté de la machine du client (et non pas du coté du serveur).
Pourquoi il ne faut pas faire confiance à Javascript si vous êtes propriétaire d’un site web
Pour comprendre, il suffit de regarder cet exemple qui peut être trouvé sur le site de certains novices.
Exemple :
function Login(){
var password=document.login.password.value;
if (password=="kztYq8") {
window.location="bravo.htm";
}
else
{
window.location="dommage.htm";
}
}
Vous l'aurez compris, le mot de passe est défini en clair dans le code source de la page. Tout le monde y a accès. L'URL censée être protégée par le mot de passe est aussi disponible et accessible sans le mot de passe! Ayez toujours en tête que ce que vous écrivez en javascript est accessible à tout le monde.
Vérifier du côté client et du côté serveur
Ce qu'on a vérifié du coté client avec le JavaScript n'est pas fiable et doit être revérifié du coté serveur (avec du PHP par exemple). En effet, en désactivant simplement le JavaScript, on peut passer outre les contrôles d'un formulaire.
Attaques de Cross-site scripting (XSS)
Lors de la création d'une application ou d'un site avec JavaScript, il faut savoir que les attaques XSS existent et sont encore couramment utilisées. En résumé, l'attaque consiste à injecter des scripts dans la réponse d'une application, ce qui permet de récupérer les données sensibles de l'application et de les envoyer sur le serveur de la personne qui est à l'origine de l'attaque. Il existe deux types d'attaque XSS:
La reflectected XSS (non permanente) : L'attaquant injecte des scripts dans une application web pour que ceux-ci soient exécutés du coté client. L'attaque n'est pas stockée dans l'application et ne laisse aucune trace sur le serveur. Pour exploiter cette faille (XSS), l'ingénierie sociale doit être utilisée afin de tromper la "future" victime. L'attaquant va fournir une URL piégée à sa victime (via mail par exemple), lorsque la victime clique sur l'URL piégée, une requête contenant du code malicieux (scripts) est envoyé vers le serveur (officiel). Le code malveillant va alors s'exécuter sur la page renvoyé par le serveur (officiel), ce qui donne à l'attaquant le contrôle dessus. Celui-ci pourra ensuite, à partir de l'application, récupérer des données sur la victime, comme par exemple sa session ou ses cookies et les recevoir directement sur son serveur. Cela lui permettra de réaliser des actions au nom de l'utilisateur ou de lui voler des informations privées.
Exemple vidéo (anglais) : ICI
La persistent XSS (permanente) : Contrairement au premier type d'attaque, celle-ci consiste à injecter le code malicieux directement dans le système de stockage de l'application Web. Pour que la faille puisse être exploitée, l'application doit générer ses réponses via ces données stockées. Elle est souvent utilisée sur des forums qui sont mal sécurisés. Ceux-ci stockent les messages des utilisateurs dans des bases de donnée. Si un message contient un code malicieux, à chaque fois qu'un utilisateur affichera la page du forum contenant le message qui présente le code malicieux, celui-ci sera exécuté. Cette attaque est donc beaucoup plus dévastatrice puisqu'elle touche potentiellement tous les utilisateurs.
Exemple vidéo (anglais) : ICI
Comment se protéger contre les attaques XSS
Il existe plusieurs moyens de se protéger, la NSA a d'ailleurs rédigé un bon document à ce propos.
Afin d'éviter que des personnes malveillantes puissent injecter du code malveillant, les administrateurs réseaux doivent traiter les informations que les personnes envoient, une fois qu'elles sont parvenues au serveur et les retraiter lors de leur envoi au navigateur. Des fonctions PHP existent afin de traiter les données envoyées via JavaScript comme "htmlentities", qui convertit tous les caractères éligibles en entités HTML et strip_tags, qui suppriment les balises HTML et PHP d'une chaîne. L'une dans l'autre permet déjà une protection de base efficace (mais pas infaillible) contre ce genre d'attaque qui exploite le JavaScript. Des mesures supplémentaires peuvent être mises en place comme l'utilisation de pare-feux applicatifs qui détectent les informations suspectes envoyées au serveur. Du coté des utilisateurs, il ne faut jamais cliquer sur un lien dont la provenance est douteuse et toujours accéder aux sites via l'URL officielle. Ils peuvent aussi utiliser des extensions comme par exemple "NoScript" sur Firefox, qui offre une bonne protection de base contre ce genre de vulnérabilité.
Références
- http://www.leblogduhacker.fr/la-securite-avec-javascript/
- https://fr.wikipedia.org/wiki/Cross-site_scripting
- https://fr.wikipedia.org/wiki/Injection_de_code_dans_les_applications_web#Cross-site_scripting_.28XSS.29
Bibliographie
- Haverbeke, M. (2015). Eloquent JavaScript. A modern Introduction to Programming. Second Edition. San Francisco, CA: no starch press. Chapitres 12 à 18