Tutoriel JavaScript côté client
Initiation à la pensée computationnelle avec JavaScript | |
---|---|
Module: Références JavaScript ◀▬ | |
◀▬▬▶ | |
⚐ à finaliser | ☸ |
⚒ 2024/08/21 | |
Voir aussi | |
Catégorie: JavaScript |
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 :
Conseil de lecture
Cette page présente une grande quantité d'information en relativement peu d'espace. Elle explique principalement les fonctionnalités, mais sans un objectif bien spécifique, ce qui peut décourager surtout les novices. Nous conseillons de lire d'abord la section JavaScript et le navigateur web et par la suite d'utiliser cette page comme référence croisée avec la page
qui propose l'explication étape par étape de quelques exemples.
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 avec le Scratchpad (SHIFT-F4).
- 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'existence 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 : 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 plutôt déconseillée.
Les trois différentes 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)
- E.g.
- 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)
- E.g.
- Relatif à la position du fichier HTML
- E.g.
<script src="../path/to/js"></source>
.
- E.g.
Les positionnements 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ée 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
Attributs des balises HTML
Pour placer du JavaScript dans les attributs HTML il faut utiliser les attributs qui se réfèrent aux événements JavaScript (voir plus bas), comme par exemple l'attribut onclick="..."
:
<button onclick="alert('J'ai cliqué sur le bouton');">Ouvrir une alerte</button>
Cette manière d'ajouter du JavaScript est plutôt déconseillée, mais reste fonctionnelle surtout pour des néophytes.
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 contenues 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>
On peut insérer plusieurs scripts, l'un après l'autre, et même combiner les fichiers externes et les scripts inline :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
</head>
<body>
<!-- HTML de la page -->
<!-- Les scripts vont ici -->
<script src="path/to/external/file.js"></script>
<script src="another/external.js"></script>
<script>
//Du code inline 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 concernent 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 possibles 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 exemple 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 possible de changer des propriétés de l'objet global window, ce qui se traduit par des comportements spécifiques qui modifient le contenu du navigateur. Vous pouvez par exemple essayer d'écrire ce code dans la Console de votre navigateur web pour être redirigé à 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 pouvez 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 cet exemple :
var num = prompt("Choisissez un nombre à ajouter à 100 pour voir le résultat", ""); console.log(100 + num);
Vous pouvez noter 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 concaténation. 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 une liste de noeuds 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"); monParagraphe.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.
<p id="monTexte">Voici un paragraphe avec du <span>texte dans un span</span></p>
Récupérer/Modifier des caractéristiques d'un élément
On peut récupérer ou modifier différentes caractéristiques d'un élément une fois identifié dans le document. Par exemple :
element.textContent
: se réfère au contenu textuel de l'élément sélectionnéelement.innerHTML
: se réfère au contenu, y compirs les balises HTML d'éléments emboîtes, de la balise de l'élément sélectionnéelement.attribute
: se réfère à un attribut spécifique de la balise de l'élément sélectionnéelement.style.property
: se réfère à une propriété de style spécifique de la balise de l'élément sélectionné
Pour récupérer il suffit de spécifier la propriété à laquelle on s'intéresse, par exemple :
//Récupérer le contenu textuel d'un element avec id="my-element"
var myElementText = document.getElementById("my-element").textContent;
//Récupérer le contenu, y compirs les balises HTML, d'un élément avec id="my-element"
var myElementHTML = document.getElementById("my-element").innerHTML;
//Récupérer la valeur d'un attribut d'un element avec id="my-element", par exemple une image avec l'attribut title
var myImageTitle = document.getElementById("my-element").title;
//Récupérer la propriété de style font-size d'un element avec id="my-element"
var myElementFontSize = document.getElementById("my-element").style.fontSize;
//veuillez noter qu'on peut pas utiliser font-size mais il faut transformer en fontSize. Ceci s'applique à toute propriété qui utilise le -
Pour modifier il faut affecter la caractéristique à une nouvelle valeur à travers le symbole d'affectation, par exemple :
//Modifier le contenu textuel d'un element avec id="my-element"
document.getElementById("my-element").textContent = "Nouveau texte de l'élément!";
//Modifier le contenu, y compirs les balises HTML, d'un élément avec id="my-element"
document.getElementById("my-element").innerHTML = "<strong>Nouveau contenu de l'élément!</strong>";
//Modifier la valeur d'un attribut d'un élément, par exemple la valeur src pour remplacer une image avec une autre
document.getElementById("my-image").src = "./my/new/path/to/image.png";
//Modifier la valeur d'une propriété de style d'un élément, par exemple sa taille
document.getElementById("my-element").style.fontSize = '40px';
Veuillez faire attention que pour les modification textContent et innerHTML vous ne pouvez pas afficher votre nouveau contenu sur plusieurs lignes, en d'autres termes CE CODE N'EST PAS VALIDE :
//CODE NON VALIDE !!!
var hello = "Hello my dear User!";
document.getElementById("my-element").innerHTML = "<div>
<p>" + hello + "</p>
</div>";
Vous avez trois alternatives :
- Tout mettre sur une seule ligne, ce qui n'est parfois pas très pratique, surtout si vous voulez insérer des valeurs symboliques à l'intérieur :
var hello = "Hello my dear User!";
document.getElementById("my-element").innerHTML = "<div><p>" + hello + "</p></div>";
- Utiliser une concatenation += :
var hello = "Hello my dear User!";
document.getElementById("my-element").innerHTML = "<div>";
document.getElementById("my-element").innerHTML += "<p>";
document.getElementById("my-element").innerHTML += hello;
document.getElementById("my-element").innerHTML += "</p>"
document.getElementById("my-element").innerHTML += "</div>";
- Utiliser les littéraux de gabarits `` (backticks en anglais) et l'interpolation ${code} (fonctionnalité introduite en 2016), voir ici pour plus d'infos
var hello = "Hello my dear User!";
document.getElementById("my-element").innerHTML = `<div>
<p>${hello}</p>
</div>`;
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 une liste de noeuds (une sorte de array), dont le premier et seul élément est notre balise body. Vu que les indices des listes commencent à 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 la liste. Cette propriété identifie le nombre d'éléments présents.
É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 serai pas affiché"); window.onload = alert("Je serai affiché"); </script>
Un autre problématique relative à cette fonction concerne le fait que l'événement onload se déclenche 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 tous les éléments interactifs associés à cet événement 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.
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 (voir événements liés à la souris plus bas).
<!DOCTYPE html>
<html>
<head>
<title>Event handling</title>
<script>
window.onload = start; //Sans () !
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.
Veuillez noter que, grâce à l'ajoute du gestionnaire d'événement window.onload
, le code JavaScript a pu être mis dans le head
du document. En spécifiant cet événement, en effet, on force le navigateur à attendre que toute la page a été téléchargée et, par conséquent, les éléments HTML seront reconnus.
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 :
var myBtn = document.getElementById("theButtonId"); myBtn.onclick = function () { console.log("The button has been clicked"); }
- Utiliser la méthode addEventListener qui prévoit deux arguments : l'événement et la fonction à exécuter :
var myBtn = document.getElementById("theButtonId"); myBtn.addEvenetListener('click', function () { console.log("The button has been clicked"); });
Dans les deux cas, la fonction associée au gestionnaire d'événements peut être :
- Une fonction anonyme (comme dans les exemples plus haut)
var myDiv = document.getElementById("importantDiv"); //version on->event myDiv.onmouseover = function() { console.log("The mouse of the user is over the importantDiv!"); } //version addEventListener myDiv.addEventListener('mouseover', function () { console.log("The mouse of the user is over the importantDiv!"); });
- Une fonction nommée
var myDiv = document.getElementById("importantDiv"); //Définir la fonction function doSomething() { console.log("The mouse of the user is over the importantDiv!"); } //L'associer à la version on->event myDiv.onmouseover = doSomething; //Attention, pas de () ! Lire plus bas pour l'explication //L'associer à la version addEventListener myDiv.addEvenetListener('mouseover', doSomething); //Encore une fois sans () !
- Une méthode d'un objet
var myDiv = document.getElementById("importantDiv"); //Définir un objet avec une méthode var o = {}; o.mouseOverHandler = function () { console.log("The mouse of the user is over the importantDiv!"); } //L'associer à la version on->event myDiv.onmouseover = o.mouseOverHandler(); //Attention, cette fois ci avec les () ! //L'associer à la version addEventListener myDiv.addEventListener('mouseover', o.mouseOverHandler()); //Encore une fois avec les () ! Faites attention aux double )) de clotûre.
La raison pour laquelle il ne faut pas utiliser les () pour une fonction nommée s'explique par le fait que avec les parenthèses la fonction serait exécutée dès que le code est lu, pas lorsque l'événement s'avère :
function faireQuelqueChose() { //code } element.onevent = faireQuelqueChose; //OK! element.onevent = faireQuelqueChose(); //Faux, de cette manière la fonction sera exécutée tout de suite, sans attendre l'événement déclencheur
Les objets, par contre, fonctionnent de manière différente : techniquement, on dit qu'ils sont passés par référence, mais la chose à retenir est tout simplement qu'avec les objets il faut mettre les parenthèses :
var o = {}: //Simule un objet quelconque o.faireQuelqueChose = function () { //code qui détermine une méthode pour l'objet } element.onevent = o.faireQuelqueChose; //Faux! element.onevent = o.faireQuelqueChose(); //OK!
Par rapport à la différence entre la version on->event
et addEventListener
, addEventListener a 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, vous pouvez :
- Affecter à
null
l'événement dans la version on->eventvar myBtn = document.getElementById("theButtonId"); //Création du gestionnaire myBtn.onclick = function () { console.log("Je ne serais pas affiché"); } //Annulation du gestionnaire myBtn.onclick = null;
- Utiliser la méthode
removeEventListener()
qui fonctionne exclusivement avec des fonctions nommées !var myBtn = document.getElementById("theButtonId"); //Définir la fonction function logEvent() { console.log("Je ne serais pas affiché"); } //Création du gestionnaire myBtn.addEventListener('click', logEvent); //Annulation du gestionnaire myBtn.removeEventListener('click', logEvent);
Comme pour la création des gestionnaires, la même différence s'applique : annuler la version on->event annule tous les événements qui sont associé, tandis que removeEventListener annule exclusivement l'exécution de la fonction particulière qui a été annulée.
L'objet event passé en argument
Lorsqu'on associe un gestionnaire d'événement, ce gestionnaire reçoit automatiquement l'objet event
en tant qu'argument. On peut utiliser cet objet pour récupérer des informations utiles sur l'événement et/ou sur l'élément qui l'a déclenché. Par exemple, on peut attribuer un listener sur l'ensemble des clicks sur body, et récupérer des informations sur l'élément spécifique qui a été cliqué :
//Add a listener for clicks anywhere in the body
document.body.addEventListener("click", clickHandler);
//The listener receives the event object
function clickHandler(event) {
//Retrieve the name of the tag clicked
var tag = event.target.tagName;
//Retrieve the content of the tag clicked
var content = event.target.innerHTML;
//Output some info about what has been clicked
document.querySelector("#output").innerHTML += "You clicked on a " + tag + " with content <strong>" + content + "</strong><br>";
}
Imaginez maintenant d'avoir dans le body le code suivant :
...
<body>
<h1>The event argument/object</h1>
<p>Click over me<!</p>
<p>Or click over me!</p>
<button>Or even here</button> <button>Or finally here</button>
<hr>
<div id="output"></div>
</body>
...
Selon l'élément que vous cliquez dans la page, vous pouvez obtenir un output du type :
- You clicked on a H1 with content The event argument/object
- You clicked on a P with content Click over me
- You clicked on a P with content Or click over me!
- You clicked on a BUTTON with content Or even here
- You clicked on a BUTTON with content Or finally here
Bloquer 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 redirigé sur le lien correspondant à l'attribut href
. JavaScript permet d'intercepter les comportements automatiques des balises et de les empêcher de se déclencher, à 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 bloquer 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 rechargé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 cliqué 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)
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