Programmation coté client en Javascript§

1

Motivation§

2

Architecture Client-Serveur§

3

Programmation coté client§

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.

4

Syntaxe§

5

Inspiration§

6

Condition§

if (i < 10) {
    j = j+1;
    k += i;
} else {
    j = 0;
}
if i < 10:
    j = j+1
    k += i
else:
    j = 0
7

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
8

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
9

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
10

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.

11

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"])
12

Pièges§

13

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;
}
14

Déclaration des variables locales§

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
15

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
16

null et undefined§

Indice

Les deux sont équivalentes à false dans une condition.

17

É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
  // ...
}
18

L'arbre DOM§

19

Présentation§

La structure d'un fichier HTML peut être vue comme un arbre.

graph  {

  node [ shape=box, style=rounded ]

  html -- head
   head -- title -- title_txt
  html -- body
   body -- h1 -- h1_txt
   body -- p
    p -- p_txt
    p -- a -- a_txt
   body -- img

  a         [ label="a\nhref='./link'" ]
  img       [ label="img\nsrc='./pic'" ]
  title_txt [ shape=box, style=filled, label="Le titre" ]
  h1_txt    [ shape=box, style=filled, label="Le titre" ]
  p_txt     [ shape=box, style=filled, label="Bonjour le " ]
  a_txt     [ shape=box, style=filled, label="monde" ]

}
20

Terminologie§

Note

Il existe d'autres types de nœuds (par exemple les nœuds commentaire), mais ils sont plus rarement utiles.

21

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 :

Note

Il est plus efficace d'utiliser les méthodes getElement* que d'utiliser querySelector* avec les sélecteurs correspondants (tagname ou #identifier).

22

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).

23

Attributs et méthodes d'un élément§

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 classe cls.
  • 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.

24

Attributs et méthodes d'un élément§

Les éléments possèdent de nombreux autres attributs; en particulier, chaque attribut HTML a une contrepartie en Javascript.

On peut notamment citer :

Expérimentez sur cet exemple.

25

Parcours du DOM§

Récupérer des nœuds depuis un élément e :

26

Modification du DOM§

Création d'un nœud :

Une fois créé, le nœud est encore hors de l'arborescence du document (et donc, non affiché). Il est nécessaire de le rattacher à un nœud parent par l'une des méthodes suivante :

27

Intégration JS dans HTML§

28

Avertissement§

Il existe de nombreuses méthodes.

Celle proposée ici vise à être simple et évolutive, mais suppose un navigateur moderne.

29

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.

30

Programmation événementielle§

31

Quelques événements utiles§

Des listes plus exhaustives sont disponibles ici et .

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.

32

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).
33

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);
34

Fonction anonyme§

35

Objets de premier niveau§

36

Fonctions anonymes§

37

Fonctions anonymes§

Exemple :

let b = document.querySelector("button");
b.addEventListener('click', function() {
  let i = document.getElementsByTagName("input")[0];
  i.value = Number(i.value) + 1;
});
38

Fonctions "flêches"§

En ES6, il existe une syntaxe plus compacte pour les fonctions anonymes. Au lieu d'écrire :

function (x, y) { return x+y; }

on peut utiliser la notation :

(x, y) => { return x+y; }

Dans le cas où la fonction comporte une unique instruction return, on peut même remplacer le corps de la fonction par l'expression à retourner :

(x, y) => x+y
39

Fonctions "flêches"§

Dans le cas où la fonction comporte exactement un argument, on peut ommettre les parenthèses autour de l'argument :

x => { console.log(x); return x+1; }

Autres exemples :

x => x+1
() => {
  console.log("Fonction flêche sans argument,");
  console.log("et comportant plusieurs instructions.");
}
40

Fonctions "flêches"§

Adaptation de l'exemple précédent :

let b = document.querySelector("button");
b.addEventListener('click', () => {
  let i = document.getElementsByTagName("input")[0];
  i.value = Number(i.value) + 1;
});

Note

Les fonctions "flêches" ne sont pas absolument équivalentes aux fonctions anonymes, mais la distinction concerne des notions non abordées dans ce chapitre.

Pour en savoir plus : https://stackoverflow.com/a/34361380

41

Modularité grâce aux fonctions anonymes§

Les variables déclarées hors de toute fonction, même préfixées par let, sont considérées par Javascript comme des variables globales.

Afin de contourner ce problème, il existe en Javascript une convention : on "enferme" tout le code du script dans une fonction anonyme, que l'on appelle immédiatement :

(function() {

// ... mon code Javascript ici ...

})();

Ainsi, on évite de polluer l'environnement global.

42

Autre exemple d'utilisation de fonctions anonymes§

let b1 = document.querySelector('button#b1');

b1.addEventListener("click", () => {
  let previousContent = b1.textContent;
  b1.textContent = "(en attente)";
  b1.disabled = true;
  setTimeout(() => {
    alert("message");
    b1.textContent = previousContent;
    b1.disabled = false;
  }, 2000);
});

Voir et modifier cet exemple.

43

Exécution asynchrone§

44

Motivation§

Dans l'exemple précédent, pourquoi utiliser la fonction setTimeout et non une fonction qui bloquerait l'exécution pendant 2s, comme on le ferait (par exemple) en Python, à l'image de l'exemple ci-dessous ?

// ⚠ MAUVAIS EXEMPLE ⚠
let previousContent = b1.textContent;
b1.textContent = "(en attente)";
b1.disabled = true;
sleep(2000); // fonction imaginaire (n'existe pas en JS)
alert("message");
b1.textContent = previousContent;
b1.disabled = false;

Note

La fonction sleep n'existe pas réellement en Javascript, et pour cause puisque ce n'est pas la bonne manière de faire.

45

Motivation§

46

Pour aller plus loin§

http://latentflip.com/loupe/

Loupe vous permet de visualiser la manière dont les événements sont gérés en Javascript.

La vidéo qui sert d'introduction est également une bonne introductions aux mécanismes mis en œuvre.

47

Portée et fermeture§

48

Portée (scope)§

49

Fermeture (closure)§

50

Gestion avancée d'événements§

51

Cheminement d'un événement§

52

Cheminement d'un événement§

Note

On peut également forcer un listener à se déclencher à la descente (capture d'un événement) plutôt qu'à sa remontée (bubbling). Pour cela on passera true en troisème paramètre de addEventListener.

53

Paramètre d'un listener§

54

Paramètre d'un listener§

document.getElementsByTagName("body")[0]
  .addEventListener("click", (evt) => {
    let msg = document.getElementById("msg");
    if (evt.target.tagName === "BUTTON") {
        msg.textContent = "Vous avez cliqué sur " +
           evt.target.textContent;
    } else {
        console.log(evt.target);
        msg.textContent = "Vous avez raté les boutons...";
    }
});

Voir et modifier cet exemple

55