Vous êtes sur la page 1sur 5

Domaine Sciences et Technologies

L2 Informatique, Luminy
WEB : Partiel
Année 2021-22

Les documents non fournis avec l’énoncé, calculatrices, téléphones, ordinateurs, etc. sont interdits

Exercice 1 Questions de cours. (4 points)


1. Une page contenant du code javascript ne donne pas le résultat prévu quand vous l’ouvrez dans le
navigateur, quel est le premier réflexe à avoir ?
Solution : Ouvrir les outils de développement web avec F12 et consulter l’inspecteur de code
HTML et la console javascript.
2. Quels est la syntaxe recommandée pour déclarer une variable x et lui assigner la valeur 0 en javascript
moderne ?
Solution :
let x = 0;
Ne pas utiliser :

var x = 0;
3. Quel est l’utilité d’ajouter un moniteur pour l’événement
'DOMContentloaded'
sur l’object document disponible côté navigateur en javascript ?
Solution : Cela permet de garantir que la page HTML courante a été entièrement chargée et en
particulier que tout ses éléments HTML sont disponibles avant que la fonction de rappel associée
au moniteur ne soit exécutée.
4. Comment indiquer au moteur javascript que nous écrivons du javascript moderne et pourquoi le faire ?
Solution : En mettant sur la première ligne du script :

"use strict";
L’intérêt est qu’en utilisant le mode strict, on se trouve forcé d’écrire du code javascript plus propre
et plus facile à déboguer et partager. Par exemple, il devient impossible d’utiliser des variables non
déclarées.

Exercice 2 Gestion d’une liste de choses à faire (TODO-list) en programmation asynchrone (16 points)

On crée un fichier TODO.html contenant le code suivant.


1 <!DOCTYPE html>
2 <html lang="en">
3

4 <head>
5

6 <meta charset=UTF-8>
7 <title>
8 TODO list
9 </title>
10

11 </head>
12

13 <body>
14

15 </body>
16

17 </html>
1. Écrire du code HTML à insérer dans le corps de lélément body pour afficher sur la page
— un titre
— un formulaire contenant un champ pour entrer du texte et un bouton de soumission (de type
submit), qu’on utilisera par la suite pour entrer de nouvelle tâches à faire dans la liste. On
donnera l’identifiant "new_TODO" au champ de texte et "submit" au bouton de soumission
— un élément div qui contiendra la liste des tâches à faire. On lui donnera l’identifiant "TODOs"
Solution :
1 <h1>My TODO list</h1>
2

3 <form>
4 <input id="new_TODO" type="text"
5 placeholder="New TODO" name="new_TODO">
6 <input type="submit" value="Submit">
7 </form>
8

9 <div id="TODOs">
10 </div>
2. Écrire du code HTML à insérer dans le corps de l’élément head qui permette d’associer la feuille de
style TODO.css et le fichier javascript TODO.js à la page TODO.html.
Solution :
1 <link rel="stylesheet" href="TODO.css">
2 <script src="TODO.js"></script>
On crée à présent le fichier javascript TODO.js contenant le code suivant.

1 "use strict";
2 document.addEventListener('DOMContentLoaded', monitor_todo_list_updates);
3. Écrire la définition de la fonction monitor_todo_list_updates qui
(a) récupère les éléments d’identifiants "TODOs", "new_TODO" ainsi que le formulaire de la page HTML
et les place dans un objet state
(b) appelle une fonction asynchrone monitor_additions, qu’on définira dans la suite, en lui passant
state en argument
Solution :
1 function monitor_todo_list_updates(event) {
2

3 const state = {
4 TODOs: document.querySelector('#TODOs'),
5 form: document.querySelector('form'),
6 new_TODO_field: document.querySelector('#new_TODO'),
7 }
8

9 monitor_additions(state);
10 }
4. Écrire la définition d’une fonction new_TODO, qui prend en argument l’élément HTML form et l’élé-
ment HTML d’identifiant new_TODO et qui renvoie une promesse qui est résolue (acceptée) lors de la
détection d’un événement 'submit' avec en argument le texte entré par l’utilisateur. On n’oubliera
pas, avant de résoudre la promesse, d’appeler la méthode preventDefault de l’objet événement ob-
tenu, afin d’éviter que la page ne soit automatiquement rafraîchie (ce qui supprimerait le contenu de
notre liste de choses à faire).

2
Solution :
1 function new_TODO(form, new_TODO_field) {
2 let p = new Promise( (accept, reject) => {
3 let callback = (event) => {
4 event.preventDefault();
5 accept(new_TODO_field.value);
6 };
7 form.addEventListener('submit', callback);
8 });
9 return p;
10 }
5. Écrire la définition d’une fonction add_TODO, qui prend en argument la variable state et le texte
correspondant à une tâche à ajouter à la liste et qui ajoute un nouvel élément div dans l’élément
d’identifiant TODOs contenant ce texte avec une marge de 20 pixels et une taille de police d’écriture
de votre choix.
Solution :
1 function add_TODO(state, todo) {
2 // create TODO
3 const div = document.createElement("div");
4 div.innerText = todo;
5 div.style.margin = "20px";
6 div.style["font-size"] = "1em";
7 state.TODOs.appendChild(div);
8 }
6. Écrire la définition de la fonction asynchrone monitor_additions, utilisant la syntaxe async/await
et appelant les fonctions new_TODO et add_TODO pour :
(a) attendre de manière non-bloquante que l’utilisateur entre une nouvelle tâche à faire et récupérer
le texte associé
(b) ajouter la nouvelle tâche à faire sur la page
(c) retourner au point (a) (c’est à dire qu’on boucle indéfiniment)

Solution :
1 async function monitor_additions(state) {
2 let todo;
3 while (true) {
4 todo = await new_TODO(state.form, state.new_TODO_field);
5 state.form.reset(); // this is nice but not absolutely necessary
6 add_TODO(state, todo);
7 }
8 }
On veut à présent permettre à l’utilisateur de supprimer des items de la liste en ajoutant un bouton pour
chaque élément sur lequel l’utilisateur peut cliquer pour déclencher l’effacement.
7. Modifier le code de la fonction monitor_todo_list_updates pour :
(a) ajouter un champ delete_monitors à l’objet state, que l’on initialisera avec un objet Map vide
(b) ajouter un appel à une fonction asynchrone monitor_deletions qui prend state en argument
et qui se chargera de gérer la détection et l’exécution des instructions d’effacement de la part de
l’utilisateur

3
Solution :
1 function monitor_todo_list_updates() {
2

3 const state = {
4 TODOs: document.querySelector('#TODOs'),
5 form: document.querySelector('form'),
6 new_TODO_field: document.querySelector('#new_TODO'),
7 delete_monitors: new Map(),
8 }
9

10 monitor_additions(state);
11 monitor_deletions(state);
12 }
8. Écrire une fonction get_click_monitor qui prend en argument un élément HTML element et un
élément HTML input, et qui retourne une promesse ainsi qu’un identifiant unique pour cette promesse
(dont on aura besoin plus tard dans les fonctions add_TODO et monitor_deletions). L’identifiant
unique pourra être obtenu par exemple par un appel à la fonction Symbol disponible en javascript, qui
ne prend pas d’argument et qui renvoie un identifiant unique (de type spécial symbol). La promesse
retournée par get_click_monitor devra être résolue (acceptée) dès que l’utilisateur clique sur le
bouton input avec en argument un tableau contenant element, input et l’identifiant unique de la
promesse.
Solution :
1 function get_click_monitor(element, input) {
2 let id = Symbol()
3 let monitor = new Promise( (accept, reject) => {
4 let callback = (event) => {
5 accept([element, input, id]);
6 };
7 input.addEventListener('click', callback);
8 });
9 return [id, monitor];
10 }
9. Modifier le code de la fonction add_TODO pour ajouter un bouton d’effacement dans l’élément div
créé et pour associer à ce bouton un moniteur d’événement et son identifiant à l’aide de la fonction
get_click_monitor. Le moniteur d’événement sera ajouté dans state.delete_monitors en utilisant
son identifiant comme index.

4
Solution :
1 function add_TODO(state, todo) {
2 // create TODO
3 const div = document.createElement("div");
4 div.innerText = todo;
5 div.style.margin = "20px";
6 div.style["font-size"] = "1em";
7 state.TODOs.appendChild(div);
8

9 // add delete button


10 const delete_button = document.createElement("input");
11 delete_button.type = "button";
12 delete_button.value = "×";
13 div.appendChild(delete_button);
14

15 // add click monitor


16 let id;
17 let monitor;
18 [id, monitor] = get_click_monitor(div, delete_button);
19 state.delete_monitors.set(id, monitor);
20 }
10. Écrire la définition de la fonction asynchrone monitor_deletions, utilisant la syntaxe async/await
qui attend de manière non bloquante que l’utilisateur demande l’effacement d’une tâche de la liste,
enlève la tâche en question de la page et recommence (indéfiniment).
Cette question est plus difficile. Prenez le temps de bien réfléchir et prenez en compte les indices
suivants.
(a) Vous pouvez utiliser la fonction Promise.race disponible en javascript qui prend en argument
un tableau de promesses et retourne une promesse qui est résolue (acceptée) dès que l’une
des promesses du tableau est résolue (avec le même argument que cette promesse). Attention
Promise.race ne s’applique que sur les éléments présents dans le tableau de promesses passé en
argument au moment de son appel.
(b) Pensez à correctement prendre en compte l’ajout d’un élément dans la liste de tâches.
(c) On peut vérifier si la valeur d’une variable x est une chaîne de caractère avec la condition
typeof x === 'string'.

Solution :

1 async function monitor_deletions(state) {


2 let detect_new, race_result, todo, delete_button, id;
3 while (true) {
4 // we reinitialize the race whenever a new item is added, so as
5 // to race over the proper set of delete_monitors at all times
6 detect_new = new_TODO(state.form, state.new_TODO_field)
7 state.delete_monitors.set("new_TODO", detect_new);
8 race_result = await Promise.race(state.delete_monitors.values());
9

10 // if a new TODO was added, we reinitialize the race


11 if (typeof race_result === 'string') continue;
12

13 // otherwise we delete the selected element from the DOM


14 // and remove the corresponding monitor from the delete_monitors list
15 [todo, delete_button, id] = race_result;
16 todo.parentNode.removeChild(todo);
17 state.delete_monitors.delete(id);
18 }
19 }

Vous aimerez peut-être aussi