Pages dynamiques en Javascript¶§
Motivation¶§
Architecture Client-Serveur¶§
- Jusqu’à maintenant, le gros du travail était fait par le serveur.
- On souhaite pouvoir déporter une partie de la logique applicative coté client.
Programmation coté client¶§
- Ceci suppose d’avoir un langage de programmation généraliste (≠ HTML/CSS) compris par tous les navigateurs.
- Actuellement, ce langage est Javascript.
Note
Javascript a beaucoup évolué au cours de son histoire.
La version que nous présentons ici est ES6, une version relativement récente (2015) qui a apporté beaucoup de nouveautés au langage, mais également des incompatibilités avec les versions précédentes.
- Gardez cela en tête lorsque vous trouverez des exemples
- en ligne.
Syntaxe¶§
Inspiration¶§
- Comme son nom l’indique, la syntaxe de Javascript est (librement) inspirirée de celle de Java (ou du C).
- La similitude s’arrête là : Javascript n’est pas basé sur Java.
Boucles¶§
while (i < 10) {
j = j*i;
i += 1;
}
while i < 10:
j = j*i
i += 1
for(let i of [1,1,2,3,5,8]) {
j = j*i;
}
for i in [1,1,2,3,5,8]:
j = j*i
for(let i=2; i<1000; i=i*i) {
console.log(i);
}
i = 2
while i<1000:
print(i)
i = i*i
Fonctions¶§
function fact(n) {
let f = 1;
while (n>1) {
f = f*n;
n -= 1;
}
return f;
}
def fact(n):
f = 1
while n>1:
f = f*n
n -= 1
return f
Exceptions¶§
if (i < 0) {
throw new Error(
"negative value");
}
if i < 0:
raise Exception(
"negative value")
try {
i = riskyFunction();
}
catch (err) {
i = -1;
}
try:
i = riskyFunction()
except Exception as err:
i = -1
Tableaux¶§
let a = [4,1,3,6,4];
let i = 1;
while (i<a.length) {
a[i] = a[i]+a[i-1];
i += 1;
}
a = [4,1,3,6,4]
i = 1
while i < len(a):
a[i] = a[i]+a[i-1]
i += 1
Avertissement
Contrairement à Python, un tableau vide (de longueur 0)
est équivalent à true
dans une condition.
Objets / Dictionnaires¶§
Note
En Javascript, ce qu’on appelle “objet” se rapproche plus des dictionnaires de Python.
let p = {
"nom": "Doe",
prénom: "John",
};
console.log(p["prénom"])
console.log(p.prénom)
p = {
"nom": "Doe",
"prénom": "John",
}
print(p["prénom"])
Pièges¶§
Javascript est un langage souvent décrié.
De fait, il comporte de nombreux pièges.
Une bonne manière de les éviter est d’utiliser systématiquement un linter tel que JSHint (intégré à de nombreux éditeurs).
Déclaration des variables locales¶§
Contrairement à Python (et à d’autres langages dynamiquement typés),
Javascript demande que les variables locales soient déclarées,
avec le mot-clé let
.
Indice
Les paramètres des fonctions font exception à cette règle, puisqu’ils sont déclarés par leur présence dans l’en-tête de la fonction.
function fact(n) {
let f = 1;
for (let i=2; i<=n; i+=1) {
f = f*i;
}
return f;
}
Avertissement
Un oubli du mot-clé let
est dangereux, car
- il ne constitue pas une erreur,
- mais dans ce cas la variable est considérée comme globale, ce qui peut créer des bugs difficiles à détecter, comme le montre cet exemple.
Note
JSHint détecte ce type d’erreur dans la plupart des cas, à condition d’activer le contrôle ainsi :
// jshint undef:true
Tests d’égalit鶧
En JS, on teste l’égalité avec l’opérateur ===
,
et l’inégalité avec l’opérateur !==
:
if (i === j && i !== k) // ...
Avertissement
Les opérateurs habituels ==
et !=
existent aussi,
mais ils ont une sémantique très inhabituelle,
et sont donc généralement évités.
Note
En fait, l’opérateur ==
considère que
deux valeurs de types différents sont égales
si on peut passer de l’une à l’autre par conversion de type.
Par exemple :
"42" == 42 // est vrai
C’est un problème, car cela conduit à des choses contre-intuitives :
if (a == b && b == c) {
a == c; // peut être faux
// par exemple: a = "0", b = 0, c = "" ;
// en effet, 0 et "" sont tous deux équivalents à false
}
ou encore :
if (a == b) {
a+1 == b+1; // peut être faux
// par exemple: a = "42" (a+1 == "421") et b = 42 (b+1 == 43)
}
JSHint signale toute utilisation de ==
ou !=
.
Si l’option n’est pas activée par défaut :
// jshint:: eqeqeq:true
null
et undefined
¶§
- En Python, il n’existe qu’une seule valeur « nulle » (
None
). - En JS, il en existe deux :
null
etundefined
. - Elles sont utilisées dans des contextes un peu différent ; pour l’instant, retenez surtout que les deux existent.
Indice
Les deux sont équivalentes à false
dans une condition.
Éléments inexistants¶§
En Javascript,
l’accès à un élément inexistant dans un tableau ou un objet
ne déclenche pas d’erreur,
mais retourne simplement undefined
.
Ceci cause en général une erreur ailleurs dans le code, ce qui rend plus difficile le débogage :
let p = { "prénom": "John" };
let n = p.nom; // n reçoit undefined, pas d'erreur
if (n.length > 32) { // erreur: undefined n'a pas de longueur
// ...
}
L’arbre DOM¶§
Terminologie¶§
- Cet arbre s’appelle l’arbre DOM (pour Document Object Model).
- Les nœuds correspondant aux balises sont appelés des éléments.
- Les nœuds contenant le contenu textuels sont simplement appelés des « nœuds texte ».
Note
Il existe d’autres types de nœuds (par exemple les nœuds commentaire), mais ils sont plus rarement utiles.
L’objet document
¶§
En JS, la variable globale document
contient un objet représentant le document HTML.
Elle permet d’accéder aux éléments du document :
- document.getElementById,
- document.getElementsByTagName,
- document.getElementsByClassName,
- document.querySelector,
- document.querySelectorAll
Note
Il est plus efficace d’utiliser les méthodes getElement*
que d’utiliser querySelector*
avec les sélecteurs correspondants
(tagname
ou #identifier
).
Attributs et méthodes d’un élément¶§
textContent
:- permet de consulter et modifier le contenu textuel de l’élément
style
:permet de consulter et modifier l’attribut
style
de l’élément, sous forme d’un objet ayant un attribut pour chaque propriété CSS. (e.g.e.style.fontSize
pour la propriétéfont-size
)Note
Pour faciliter l’utilisation en Javascript, la typographie des attributs de
style
n’est pas la même que celle des propriétés CSS correspondantes. Les tirets (-
) sont remplacés par une mise en majuscule de la letter suivante (CamelCase).
classList
:permet de consulter et modifier l’attribut
class
de l’élément, grâce aux méthodes suivantes :add(cls)
: ajoute la classe cls a l’élément.remove(cls)
: retire la classe cls a l’élément.contains(cls)
: indique si l’élément possède actuellement la classecls
.toggle(cls)
: inverse l’état de la classe cls (présente/absente) sur l’élément.
Note
Comme en HTML+CSS, il est préférable de spécifier la mise en forme à l’aide de classes dans le CSS, et de modifier ces classes dans le code Javascript, plutôt que la spécifier directement dans le code Javascript à travers l’attribute
style
.
Les éléments possèdent de nombreux autres attributs; en particulier, chaque attribut HTML a une contrepartie en Javascript.
On peut notamment citer :
href
(pour les<a>
)src
(pour les<img>
)value
(pour les<input>
)disabled
(pour tous les éléments de formulaire)checked
(pour les cases à cocher)- etc...
Expérimentez sur cet exemple.
Intégration JS dans HTML¶§
Avertissement¶§
Il existe de nombreuses méthodes.
Celle proposée ici vise à être simple et évolutive, mais suppose un navigateur moderne.
Intégration d’un script à une page¶§
On include dans le HTML (dans le head
ou le body
)
une balise script
ayant la structure suivante :
<script src="url_du_script.js" defer></script>
Le script sera exécuté après le chargement complet du code HTML.
Programmation événementielle¶§
- En programmation impérative classique,
la fonction principale (
main
) décrit dans quel ordre les différentes fonctions du programme doivent s’exécuter. - En programmation événementielle, on « abonne » chaque fonction à un (ou plusieurs) événement(s).
- La fonction s’exécute lorsqu’un événement auquel elle est abonnée se produit.
- Les événement sont (souvent) liés aux interactions de l’utilisateur avec l’application.
Quelques événements utiles¶§
Des listes plus exhaustives sont disponibles ici et là.
Note
Souvent, et pour des raisons historiques,
les noms des événements sont préfixés par on
.
Attention : le véritable nom de l’événement n’inclut pas ce préfixe.
Mise en œuvre¶§
function incrementCounter() {
let i = document.getElementsByTagName("input")[0];
i.value = Number(i.value) + 1;
}
let b = document.querySelector("button");
b.addEventListener('click', incrementCounter);
http://jsbin.com/mexuna/1/edit?html,js,output
Note
- La méthode
addEventListener
associe un comportement (fonction) à un événement émis par un élément. - ⚠ Attention : le deuxième paramètre de
addEventListener
est le nom d’une fonction, sans parenthèses (ce n’est pas un appel de la fonction).
Autre exemple¶§
function incrementCounter() {
let b = document.getElementsByTagName("body")[0];
let p = document.createElement("p");
p.textContent = "Vous avez cliqué";
p.class.add("message");
b.appendChild(p);
}
let b = document.querySelector("button");
b.addEventListener('click', incrementCounter);