b) Interactions avec le DOM

YoutubeImage

C'est quoi le DOM ?

Le Document Object Model (DOM) est une structure de données, sous forme d'arbre, représentant les éléments HTML d'une page web.

Une page web est automatiquement réaffichée quand une manipulation JS met à jour un objet issu du DOM.

Le DOM donne accès aux éléments HTML ainsi qu'à leur style. Il est donc possible de mettre à jour tant l'HTML que le CSS d'une page web.

GatsbyImage

HTML DOM Tree [R.22]

Comment accéder à un élément HTML en JS ?

Introduction

Il existe plusieurs moyens d'accéder à un élément HTML via des méthodes offertes par le DOM.
Nous allons voir ensemble les façons les plus pratiques.

querySelector()

Cette méthode retourne le 1er élément qui correspond à un sélecteur CSS.

Voici un exemple :

html
<button id="myBtn2">Click please</button>
<div class="message"></div>

Voici le code JS permettant d'accéder au éléments donnés ci-dessus :

js
let btn2 = document.querySelector("#myBtn2"); // HTML id attribute
let msg = document.querySelector(".message"); // CSS class name
let duplicateMsg = document.querySelector("div"); // HTML tag name

Les CSS sélecteurs sont importants en JS, tout comme en CSS :

  • # : permet l'accès via la propriété html id.
  • . : permet l'accès via une classe qui aurait été donnée à l'élément HTML (via la propriété class).
  • TAG_NAME : permet l'accès à un élément via le nom de la balise associée à l'élément HTML.

Notons que pour modifier un élément HTML pour lequel nous avons obtenu une référence, il suffit de changer une de ses propriétés. On utilise pour cela le nom des propriétés associées à l'élément HTML. Par exemple, si l'on souhaite changer le texte de btn2, il suffit de changer sa propriété innerText ou innerHTML :

js
btn2.innerText = "I have been clicked";

Une fois cette ligne exécutée par le browser, comme le DOM a été modifié, le bouton serait automatiquement réaffiché avec le nouveau texte "I have been clicked".

getElementById()

Cette méthode retourne l'élément dont la propriété HTML nommée id contient la valeur donnée en paramètre.

Voici un exemple :

html
<button id="myBtn1">Click please</button>

Voici comment on pourrait accéder à cet élément :

js
let btn1 = document.getElementById("myBtn1");

querySelectorAll()

Cette méthode retourne tous les éléments qui correspondent à un sélecteur CSS, sous forme d'une NodeList.

Voici un exemple :

html
<button id="myBtn1">Click please</button>
<button id="myBtn2">Click please</button>
<button id="myBtn3">Click please</button>

Voici comment on pourrait accéder à ces éléments afin de mettre à jour leurs textes :

js
const buttons = document.querySelectorAll("button");
buttons.forEach((btn) => {
btn.innerText = "Hacked!";
});

Autres méthodes

Il existe bien sûr d'autres méthodes pour accéder aux éléments HTML.

Nous pensons que si vous retenez querySelector & querySelectorAll, il n'est pas nécessaire d'en connaître d'autres.

Néanmoins, n'hésitez pas à explorer la documentation MDN sur l'interface Document [23.].

La programmation événementielle

Pour réaliser une bonne IHM, nous allons faire de la programmation événementielle. Nous allons écrire du code pour décrire ce qui doit être fait en fonction d'un clic, du passage d'une souris...

On va ajouter des écouteurs d'événements ("event listeners") sur des éléments du DOM pour définir des actions qui seront à réaliser une fois l'événement soulevé.

Vous avez déjà expérimenté des événements qui amènent à des actions par défaut, par exemple :

  • lors d'un clic sur un bouton de type "submit" d'un formulaire : les données sont envoyées au serveur et la page est rafraichie.
  • lors d'un clic sur un "hyperlink" : on est redirigé vers une nouvelle page (où l'on voyage parfois aussi à l'intérieur de la même page).

Certains types d'événements sont dits "cancelable events". Pour ces événements, il est possible de stopper l'action par défaut. Nous allons prochainement découvrir comment nous pourrions empêcher de rafraichir une page lors du clic sur le bouton "submit" d'un formulaire.

Flux des événements du DOM

Il est possible que plusieurs écouteurs d'événements soient actifs au sein d'une IHM.

Voici comment le browser traite les événements :

  1. Dans la phase de capture : l'événement est capturé et propagé des éléments ancêtres vers l'élément cible de l'événement.
  2. Dans la "target phase" : l'événement est géré par l'élément cible.
  3. Dans la "bubbling phase" : l'événement est propagé de l'élément cible vers ses ancêtres qui pourront eux-aussi gérer cet événement si nécessaire.
GatsbyImage

Flux de traitement d'un événement [R.26]_

Les écouteurs d'événements

Introduction

Il est possible de gérer des événements de plusieurs façons.

Via des méthodes

Vous pouvez ajouter des écouteurs d'événements à l'aide de la méthode addEventListener().

Vous pouvez enlever des écouteurs d'événements à l'aide de la méthode removeEventListener().

Voici un exemple :

html
<html>
<head>
<title>Demo : click event management to update a button</title>
</head>
<body>
<button id="myBtn1">Click please</button>
<button id="myBtn2">Click please</button>
<button id="myBtn3">Remove extra actions on button 2</button>
<button id="myBtn4">Click please</button>
<script src="index.js"></script>
</body>
</html>

Voici comment on pourrait gérer des clics sur ces boutons :

js
const btn1 = document.querySelector('#myBtn1');
const btn2 = document.querySelector('#myBtn2');
const buttons = document.querySelectorAll('button');
const btn3 = buttons[2]; // fancy way to get a reference...
const btn4 = document.querySelector('#myBtn4');
btn1.addEventListener('click', () => {
btn1.innerText = 'myBtn1 : I have been clicked !';
console.log('onClickHandlerForBtn1::click');
});
btn2.addEventListener('click', onClickHandlerForBtn2);
btn2.addEventListener('click', onClickHandlerForBtnExtra);
btn3.addEventListener('click', onClickHandlerForBtn3);
function onClickHandlerForBtn2() {
btn2.innerText = 'myBtn2 : I have also been clicked';
console.log('onClickHandlerForBtn2::click');
}
function onClickHandlerForBtn3() {
btn2.removeEventListener('click', onClickHandlerForBtnExtra);
console.log('onClickHandlerForBtn3::click');
}
function onClickHandlerForBtnExtra() {
console.log('onClickHandlerForBtnExtra::click');
}
btn4.onclick = function() {
btn4.innerText = 'myBtn4 : You clicked on me : )';
console.log('onClickHandlerForBtn4::click');
};

N'hésitez pas à exécuter ce code.

Comment faire ?

Au sein de votre répertoire /web2/tutorial, vous pourriez créer un répertoire dom-events, et y ajouter un fichier index.html contenant l'HTML ci-dessus.

Puis vous pourriez ajouter un fichier index.js contenant le JS ci-dessus.

Utilisez Live Server pour lancer index.html et accédez à la console de votre browser pour voir ce qui se passe lors de clic sur chacun des boutons.

Via des propriétés

👎 Il est aussi possible d'ajouter des écouteurs d'événements à l'aide de propriétés, mais nous ne le recommandons pas.

js
btn4.onclick = function () {
btn4.innerText = "You clicked on me : )";
console.log("btn.onclick::anonymous function");
};

Sur internet, vous trouverez beaucoup d'exemples de gestion d'événements à l'aide de propriétés.

👍 Si vous veniez à utiliser ce genre de code, nous vous recommandons un refactor vers l'utilisation de addEventListenner.

Les callbacks & la programmation fonctionnelle

Les écouteurs d'événements ont besoin de préciser une action asynchrone, une action qui sera à réaliser le moment venu.

Pour spécifier cette action asynchrone, nous faisons ce que nous appelons de la programmation fonctionnelle. A la fonction addEventListenner, nous lui passons en paramètre une autre fonction que nous appelons une callback.

Une callback sera donc la fonction qui sera appelée le moment venu. Dans le cadre de la fonction addEventListenner, la callback sera appelée lorsque l'événement sera traité par l'élément HTML cible.

Dans ce code-ci :

js
1
const btn1 = document.querySelector('#myBtn1');
2
const btn2 = document.querySelector('#myBtn2');
3
const buttons = document.querySelectorAll('button');
4
const btn3 = buttons[2]; // fancy way to get a reference...
5
const btn4 = document.querySelector('#myBtn4');
6
7
btn1.addEventListener('click', () => {
8
btn1.innerText = 'myBtn1 : I have been clicked !';
9
console.log('onClickHandlerForBtn1::click');
10
});
11
btn2.addEventListener('click', onClickHandlerForBtn2);
12
btn2.addEventListener('click', onClickHandlerForBtnExtra);
13
btn3.addEventListener('click', onClickHandlerForBtn3);
14
15
function onClickHandlerForBtn2() {
16
btn2.innerText = 'myBtn2 : I have also been clicked';
17
console.log('onClickHandlerForBtn2::click');
18
}
19
20
function onClickHandlerForBtn3() {
21
btn2.removeEventListener('click', onClickHandlerForBtnExtra);
22
console.log('onClickHandlerForBtn3::click');
23
}
24
25
function onClickHandlerForBtnExtra() {
26
console.log('onClickHandlerForBtnExtra::click');
27
}
28
29
btn4.onclick = function() {
30
btn4.innerText = 'myBtn4 : You clicked on me : )';
31
console.log('onClickHandlerForBtn4::click');
32
};

On voit que les callback, le 2ème paramètre d'addEventListener, peuvent être soit des fonctions "arrow" ou anonymes, soit des fonctions nommées.

⚡ Lorsque vous indiquez une callback, il est important que vous omettiez les parenthèses de la fonction. Sinon, cela ne fonctionnera pas ! Vous devez donner la définition d'une fonction, pas l'appel d'une fonction !

Création d'une IHM classique

Introduction

Tout au long du cours, nous développons de manière incrémentale, via une série de tutoriels, un site pour une pizzeria.

Dans votre repo web2, veuillez créer le répertoire /tutorials/pizzeria/hmi/classic.

Nous allons maintenant développer la version classique du site d'une pizzeria dans le dossier classic.

Veuillez ajouter dans le dossier classic ces 3 répertoires :

  • /tutorials/pizzeria/hmi/classic/img : c'est là que seront reprises les photos ;
  • /tutorials/pizzeria/hmi/classic/sound : c'est là que les fichiers sonores seront ajoutés ;
  • /tutorials/pizzeria/hmi/classic/stylesheets : c'est là que le CSS sera donné.

Par la suite de ce tutoriel, nous considérons que tous les chemins absolus démarrent du répertoire /tutorials/pizzeria/hmi/classic (ou /web2/tutorials/pizzeria/hmi/classic si l'on considère le nom du répertoire du repo).

Veuillez ajouter ces 3 images dans /img :

Veuillez ajouter la musique du site dans /sound :

Pizza Spinoza [R.30]

Les abréviations Emmet

Nous allons d'abord créer la structure des éléments de la page au sein d'un nouveau fichier /index.html.

Pour générer le corps de sa page HTML, on peut utiliser une abréviation Emmet au sein de VS Code ; pour cela, commencez à taper html:5 et utilisez l'autocompletion (ou saisie automatique) dans le fichier index.html (il suffit de cliquer sur html:5 qui apparaît dans la liste associée à l'autocompletion). Donnez le titre Pizzeria à votre page web.

Les abréviations Emmet permettent donc d'automatiser l'écriture d'HTML.

En savoir plus sur les abréviations Emmet : https://docs.emmet.io/abbreviations/ [R.31]

Création de la structure de l'IHM

Nous allons ajouter un header, un main et un footer à notre page web.

Le header reprendra un simple texte ("We love Pizza").

Le main contiendra seulement une balise audio pour offrir un lecteur contenant la chanson Pizza Spinoza.

Le footer présentera un texte ("But we also love JS") et le logo de JS.

Voici ce que devrait donner à ce stade-ci votre index.html :

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pizzeria</title>
</head>
<body>
<header>
<h1>We love Pizza</h1>
</header>
<main>
<audio id="audioPlayer" controls autoplay>
<source
src="./sound/Infecticide-11-Pizza-Spinoza.mp3"
type="audio/mpeg"
/>
Your browser does not support the audio element.
</audio>
</main>
<footer>
<h1>But we also love JS</h1>
<img src="./img/js-logo.png" alt="" />
</footer>
</body>
</html>

Veuillez exécuter cette page web. Même si ce n'est pas très esthétique, tous les éléments sont présents.

Mise en forme

A l'aide de CSS, nous allons mettre en forme notre page web via un nouveau fichier à créer : /stylesheets/style.css.

Nous souhaitons que le layout de la page fasse toujours, au minimum, la hauteur complète du navigateur ; pour ce faire nous allons :

  • utiliser un body repris comme un un élément Flexbox faisant 100% de hauteur.
  • paramétrer le main pour lui permettre de remplir l'espace disponible.

Ainsi, le footer donnera l'effet d'être "sticky" en bas de page.

De plus, nous allons ajouter pizza.jpg comme background de l'élément html.

Voici le code de /stylesheets/style.css (veuillez l'ajouter dans votre fichier) :

css
html, body {
height: 100%;
margin: 0;
}
body {
/*to easily deal with sticky footer*/
display: flex;
flex-direction: column;
background-image : url(../img/pizza.jpg);
background-size : cover;
}
header{
text-align: center;
}
main{
text-align: center;
/*to easily deal with sticky footer:
grow the main to fill the space*/
flex: 1 0 auto;
}
footer{
text-align: center;
}
footer img {
height: 50px;
}
footer h1{
color: white;
}

Pour appliquer la stylesheet à index.html, il suffit d'ajouter cette balise dans index.html à la fin de la balise head :

html
<link rel="stylesheet" href="./stylesheets/style.css" />

Pour ajouter cette balise, il est possible de juste taper l'abréviation Emmet "link:css" dans VS Code. Il ne reste plus qu'à ajouter le chemin vers le fichier au sein de href. Dans la valeur de href, si vous tapez ./, vous verrez tous les fichiers et répertoires disponibles.

👍 Il est recommandé d'utiliser un chemin relatif dans href et d'utiliser la saisie automatique (autocompletion) afin de ne pas se tromper dans l'écriture du chemin d'un fichier.

Il est temps d'ajouter le fichier favicon comme logo pour notre site.

Tapez l'abréviation Emmet link:favicon à la fin de la balise head d'index.html.
Une balise de ce genre doit avoir été ajoutée :

html
<link
rel="shortcut icon"
href="./img/pizza-svgrepo-com.svg"
type="image/svg+xml"
/>

La page web de la pizzeria devrait paraître pas trop mal à ce stade ci.

Ajout d'interactivité

Nous souhaitons maintenant rendre cette page un peu plus interactive.

Nous nous sommes rendu compte que les clients de la pizzeria ont des difficultés à cliquer juste sur le bouton Play/Pause du lecteur.

Dès lors, nous allons permettre aux visiteurs de cliquer n'importe où sur la page pour démarrer ou stopper la musique.

Nous allons ajouter un script index.js gérant les clics des utilisateurs sur la page :

js
const body = document.querySelector("body");
body.addEventListener("click", startOrStopSound);
function startOrStopSound() {
const myAudioPlayer = document.querySelector("#audioPlayer");
if (myAudioPlayer.paused) myAudioPlayer.play();
else myAudioPlayer.pause();
}

Ici, à l'aide d'un script JS et du DOM, nous pouvons accéder à l'HTMLAudioElement contenu dans la page web. Nous avons ensuite accès à toutes les propriétés et fonctions mises à disposition par cet objet.

Pour ajouter le fichier JS externe à la page HTML à la fin de la balise body, vous pouvez utiliser une abréviation Emmet "script:src" et il ne vous restera plus qu'à indiquer le chemin vers index.js au sein de src.

html
<script src="./index.js"></script>

Voila, vous devriez pouvoir bien exécuter la HomePage du site de la pizzeria qui traite des clics n'importe où sur la page.

Ajout d'une librairie externe

Nous souhaiterions ajouter une librairie externe à notre application et l'utiliser afin d'afficher une petite animation sur notre HomePage.

Après quelques recherches dans google, nous découvrons la librairie Animate.css [R.32].

En lisant la documentation, nous voyons que pour ajouter la librairie à notre application, il suffit d'ajouter cette balise à une page HTML :

html
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>

Et pour utiliser cette librairie, nous pouvons avoir un titre qui rebondit en utilisant une classe CSS de cette façon, directement au sein d'une page HTML :

html
<h1 class="animate__animated animate__bounce">We love Pizza</h1>

Toujours en lisant la documentation, si nous souhaitons ajouter un délai au rebondissement, nous pouvons le faire de cette façon, à l'aide d'une classe CSS :

html
<h1 class="animate__animated animate__bounce animate__delay-2s">
But we also love JS
</h1>

Veuillez mettre à jour votre page index.html :

html
1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
<title>Pizzeria</title>
7
<link rel="icon" href="./img/pizza-svgrepo-com.svg" type="image/svg+xml" />
8
<link rel="stylesheet" href="./stylesheets/style.css" />
9
<link
10
rel="stylesheet"
11
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
12
/>
13
</head>
14
<body>
15
<header>
16
<h1 class="animate__animated animate__bounce">We love Pizza</h1>
17
</header>
18
19
<main>
20
<p>My HomePage</p>
21
<p>
22
Because we love JS, you can also click on the header to stop / start the
23
music ; )
24
</p>
25
26
<audio id="audioPlayer" controls autoplay>
27
<source
28
src="./sound/Infecticide-11-Pizza-Spinoza.mp3"
29
type="audio/mpeg"
30
/>
31
Your browser does not support the audio element.
32
</audio>
33
</main>
34
35
<footer>
36
<h1 class="animate__animated animate__bounce animate__delay-2s">
37
But we also love JS
38
</h1>
39
<img src="./img/js-logo.png" alt="" />
40
</footer>
41
42
<script src="./index.js"></script>
43
</body>
44
</html>

Nous avons très facilement ajouté deux animations à notre application web sans effort, en nous reposant sur une librairie externe.

Si tout fonctionne bien, faites un commit de votre repo (web2) avec comme message : classic-hmi tutorial.

En cas de souci, vous pouvez accéder au code final de ce tutorial ici : classic-hmi.

Exercice 2.2 : interaction avec le DOM

Veuillez réaliser une application web permettant d'afficher un compteur de clics sur votre page web.

Affichez le message suivant sur votre page web :

  • Au 5ème clic jusqu'au 9ème clic : "Bravo, bel échauffement !"
  • Au 10ème clic et au-delà : "Vous êtes passé maître en l'art du clic !"

N'oubliez pas, bien sûr, d'afficher le compteur de clics sur votre page.

Veuillez créer un nouveau projet dans votre repository local et votre web repository (normalement appelé web2) nommé /exercises/2.2. Quand votre application est finalisée, veuillez faire un commit de votre code avec comme message : 2.2 : HMI : dom interaction.

🤝 Tips

  • Vous avez besoin d'un container pour afficher de l'information. Vous pouvez utiliser la propriété innerHtml ou textContent d'un paragraphe <p> ou d'un <span> pour afficher votre compteur.
  • Vous pouvez faire de même pour afficher votre message.
  • Vous pouvez ajouter un gestionnaire d'événement sur l'objet window de votre navigateur.

L'objet event

YoutubeImage

L'objet event est automatiquement passé à la callback d'un écouteur d'événement.

Il est très utile, pour deux raisons principalement :

  • Stopper l'action par défaut suite à un événement.
  • Lorsqu'on attache une même callback à une multitude d'éléments, pour retrouver la cible de l'événement.

Voici un exemple où l'on souhaite découvrir l'id de la div lorsque la souris quitte celle-ci. index.html contient 6 divs :

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Demo : mouse-over event management to update a div</title>
<link rel="stylesheet" href="./main.css">
<body>
<div class="message" id="msgBox1">
Waiting for the call to a welcome function...
</div>
<div class="message" id="msgBox2">
Waiting for the call to a welcome function...
</div>
<div class="message" id="msgBox3">
Waiting for the call to a welcome function...
</div>
<div class="message" id="msgBox4">
Waiting for the call to a welcome function...
</div>
<div class="message" id="msgBox5">
Waiting for the call to a welcome function...
</div>
<div class="message" id="msgBox6">
Waiting for the call to a welcome function...
</div>
<script src="index.js" ></script>
</body>
</html>

Un peu de style pour bien visualiser les 6 divs au sein de main.css :

css
.message {
padding: 1rem;
margin: 1rem;
border: solid 1px black;
}

Voici comment on pourrait gérer les mouvements de la souris au sein de index.js :

js
const divs = document.querySelectorAll(".message");
divs.forEach((div) => {
div.addEventListener("mouseover", () => {
div.innerText = "Hello world!";
});
div.addEventListener("mouseout", (e) => {
div.innerText = `You have left the div wit id ${e.target.id}`;
});
});

L'objet event a été nommé e ici, mais nous aurions pu lui donner le nom que l'on souhaitait.

👍 Pour éviter la confusion, il est recommandé de l'appeler e (ou éventuellement event).

e.target est l'élément HTML qui lance la propagation de l'événement dans le DOM tree.

Parfois on préfère utiliser e.currentTarget qui est l'élément HTML sur lequel est attaché l'écouteur d'événements.

N'hésitez pas à exécuter le code ci-dessus, si vous le souhaitez, en créant les 3 fichiers nécessaires.

Voici un exemple partiel de gestion de formulaire où l'on utilise l'objet event pour stopper l'action par défaut qui est d'envoyer les données au serveur (indiqué par la propriété action du formulaire) et de recharger la page :

js
// code to get the reference to the form shall be imagined
const onSubmit = (e) => {
console.log("onSubmit::");
e.preventDefault();
};
myForm.addEventListener("submit", onSubmit);

Exercice 2.3.1 : objet event & formulaire

Veuillez réaliser une application web permettant d'afficher un formulaire demandant votre souhait de l'instant et un bouton pour le soumettre.

Veuillez créer un nouveau projet dans votre repository local et votre web repository (normalement appelé web2) nommé /exercises/2.3.1.

Lorsqu'on clique pour soumettre son souhait, veuillez faire disparaître le formulaire et afficher le souhait soumis. Attention, vous devez vous assurer que la page n'est pas rechargée lors de la soumission du formulaire.

Quand votre application est finalisée, veuillez faire un commit de votre code avec comme message : 2.3.1 : event object & form.

🤝 Tips

  • Pour faire disparaître un container (une div par exemple), vous pourriez utiliser sa propriété style.display et la mettre à none.
  • Pour faire apparaître du contenu dans un container, (une div par exemple), vous pourriez utiliser sa propriété innerText.

🍬 Si vous souhaitez aller plus loin

Une fois un souhait fait, vous pourriez afficher un bouton permettant de réafficher le formulaire demandant un souhait.

Exercice 2.3.2 : objet event & multiples éléments

Veuillez réaliser une application web permettant d'afficher la couleur associée à une div parmi environ un millier de divs.

Veuillez créer un nouveau projet dans votre repository local et votre web repository (normalement appelé web2) nommé /exercises/2.3.2.

La page HTML contenant environ un millier de divs vous est offerte : Fichier HTML de base

Il ne vous reste plus qu'à ajouter l'interactivité :

  • Quand on clique sur une div, vous devez l'agrandir et afficher à l'intérieur sa couleur (elle peut apparaître en RGB, par exemple : rgb(255. 153. 255)).
  • Vous devez faire cela en utilisant l'objet event.

Quand votre application est finalisée, veuillez faire un commit de votre code avec comme message : 2.3.2 : event object & multiple divs.

🤝 Tips

  • N'hésitez pas à récupérer une liste de toutes les divs via querySelectorAll.
  • Vous pourriez, sur base de cette liste, attacher un écouteur de clics sur chaque div : addEventListener.
  • La callback passée à addEventListener vous donne accès à la cible de l'événement via l'objet event.

🍬 Si vous souhaitez aller plus loin

Si vous le souhaitez, vous pourriez faire en sorte que lorsqu'on clique une nouvelle fois sur une div, celle-ci revienne à sa taille initiale et n'affiche plus sa couleur.

Les timers & actions

YoutubeImage

setTimeout(f,t) permet l'exécution d'une callback f à l'expiration d'un timer, après t ms.

clearTimeout() permet de stopper l'exécution d'une callback qui a été appelée via setTimeout().

Voici un exemple :

html
<html>
<head>
<title>Demo : timer management</title>
</head>
<body>
<button id="myBtn1">Show pop-up after 2s timer</button>
<button id="myBtn2">Clear planned action</button>
<script src="index.js"></script>
</body>
</html>
js
1
const btn1 = document.querySelector("#myBtn1");
2
const btn2 = document.querySelector("#myBtn2");
3
4
btn1.addEventListener("click", delayedAlert);
5
btn2.addEventListener("click", clearAlert);
6
7
let timeoutID;
8
const delayInSeconds = 2;
9
const delayInMiliSeconds = delayInSeconds * 1000;
10
11
function delayedAlert() {
12
timeoutID = setTimeout(() => {
13
alert(`You asked for this popup ${delayInSeconds}s ago!`);
14
}, delayInMiliSeconds);
15
}
16
17
function clearAlert() {
18
clearTimeout(timeoutID);
19
}

N'hésitez pas à exécuter ce code, si vous le souhaitez, en créant les 2 fichiers nécessaires.

Normalement, si vous cliquez sur le 1er bouton, puis avant deux secondes sur le deuxième bouton, aucun pop-up ne sera lancé.

Exercice 2.4.1 : timer pour lancer une action

Veuillez réaliser une application web permettant d'afficher le temps qu'il faut pour qu'un utilisateur clique 10 fois sur un bouton :

  • Le jeu démarre dès que la souris de l'utilisateur passe sur le bouton.
  • Si 5 secondes après que l'utilisateur soit passé sur le bouton, l'utilisateur n'a pas terminé son 10ème clic, veuillez afficher un message de type : "Game over, you did not click 10 times within 5s !".
  • Si l'utilisateur a terminé ses 10 clics avant 5s, veuillez afficher :"You win ! You clicked 10 times within X ms" où X est une valeur entière.

Veuillez créer un nouveau projet dans votre repository local et votre web repository (normalement appelé web2) nommé /exercises/2.4.1.

Quand votre application est finalisée, veuillez faire un commit de votre code avec comme message : 2.4.1 : run action within timer.

🤝 Tips

  • Quelle action doit être programmée dans le temps ? Peut-être celle en cas d'échec, lorsque le temps est écoulé...
  • Quid si l'utilisateur réussi à cliquer suffisamment vite ? Peut-être faut il annuler l'action programmée...

Les intervalles de temps & actions répétées

setInterval(f,t) permet l'exécution d'une callback f tous les t ms.

clearInterval() permet de stopper les appels à la callback qui ont été programmés via setInterval().

On souhaiterait programmer une horloge, voici un simple exemple :

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo : interval management to provide a clock</title>
</head>
<body>
<span></span>
<br />
<button>Stop or resume the clock</button>
<script src="./index.js"></script>
</body>
</html>
js
1
const btn = document.querySelector("button");
2
const clockHolder = document.querySelector("span");
3
4
btn.addEventListener("click", stopOrResumeClock);
5
6
var myIntervalId;
7
8
startClock();
9
10
function startClock() {
11
myIntervalId = setInterval(printCurrentTime, 1000);
12
}
13
14
function printCurrentTime() {
15
const now = new Date();
16
const time = now.toLocaleTimeString();
17
clockHolder.innerText = time;
18
}
19
20
function stopOrResumeClock() {
21
if (myIntervalId) {
22
clearInterval(myIntervalId);
23
myIntervalId = undefined;
24
} else startClock();
25
}

N'hésitez pas à exécuter ce code si vous le souhaitez en créant les 2 fichiers nécessaires.

La gestion d'intervalles, de cette façon, est l'ancêtre des animations en JS !

Si vous souhaitez consulter la documentation MDN concernant la gestion d'événements, afin d'approfondir certains éléments, la voici :

Exercice 2.4.2 : séquence d'événements différés dans le temps

Veuillez réaliser une application web permettant d'afficher un feu rouge.

Votre feu rouge devra continuellement alterner l'affichage de ce cycle de trois couleurs :

  • rouge pendant 2 secondes
  • orange pendant 2 secondes
  • vert pendant 2 secondes
  • orange pendant 2 secondes
  • rouge pendant 2 secondes
  • ...

Voila à quoi pourrait ressembler votre application web :

Veuillez créer un nouveau projet dans votre repository local et votre web repository (normalement appelé web2) nommé /exercises/2.4.2. Quand votre application est finalisée, veuillez faire un commit de votre code avec comme message : 2.4.2 : timed events.

🤝 Tips

  • Vous avez besoin d'un container pour afficher une couleur. Vous pourriez utiliser la propriété style.backgroundColor d'une div.
  • Comment afficher une div vide avec une bordure ? N'hésitez pas à vous inspirer de ce morceau de code :
js
<div
class="red"
style="display: inline-block; min-width: 50px; min-height: 50px; border:1px solid black"
></div>

Introduction au projet

Dans le cadre de ce cours, nous souhaitions que vous puissiez vous exercer au développement d'une application qui vous tient à coeur.

C'est pourquoi, nous vous demandons de réfléchir à une application que vous souhaiteriez développer.

Pour aborder certains concepts, vous allez créer une application web mettant en œuvre :

  • Une thématique qui vous tient à cœur ;
  • Une RESTful API tournant sous Node.js & Express ;
  • Un frontend animé utilisant les opérations offertes par votre RESTful API et éventuellement des APIs tierces.

Pour votre frontend animé, l'animation sera en 2D, 3D, sous forme de jeux ou de simples effets visuels.

A ce stade-ci, nous vous proposons d'écrire l'objectif du projet que vous souhaitez développer et d'identifier un titre pour votre projet. La description de votre projet (~15 lignes) devrait répondre à ces questions :

  • quel type d'organisation (équipe sportive, bibliothèque, amis, famille…) serait prise en charge par votre application ?
  • Pourquoi cette application vous tient à cœur ?

Au sein de votre répertoire /web2/, veuillez créer un répertoire /web2/project, et y ajouter un fichier README.md contenant le titre du projet qui vous tiendrait à coeur de développer ainsi que la courte description de son objectif.

Pour décider de l'objectif de votre projet, vous pouvez vous inspirer de ce qui a été fait les années précédentes par les étudiants de Vinci en consultant la vitrine de projets.

Voici quelques autres exemples d'applications que vous pourriez réaliser :

  • une gestion de critiques de livres ;
  • une gestion d'une équipe sportive (gestion des achats ou autres de l'équipe) ;
  • un jeux vidéo (multi-joueurs ou pas) dont les utilisateurs et leurs scores seraient gérés par une RESTful API ;
  • Un jeu de devinette d'un mot sur base du dessin d'un des joueurs ;

Quand votre README est finalisé, veuillez faire un commit avec comme message : 2.6 : project description & title.