CentraleSupélecDépartement informatique
Gâteau du Glouton
3 rue Joliot-Curie
F-91192 Gif-sur-Yvette cedex
TD Mercredi — JavaScript

L'objectif de ce TD est de se familiariser avec le langage javascript. On en profitera pour manipuler un format d'image XML : le SVG.

Pour commencer:

Pour rendre votre travail

Sur le site Edunao.

Pour réaliser les exercices

Il vous faut au minimum une console javascript. Tous les navigateurs en proposent une avec les outils de développement.

Avec Firefox, puisque c'est notre outil par défaut, pour ouvrir la console javascript: Control-Shift-K, ou dans le menu: "outils de développement web" puis "console".

Sinon, avec Repl.it, la zone noire en bas à droite est la console javascript de la page. Pour les besoins du TD c'est le plus simple, cela permet de garder une trace de ce qui est fait.

1 - Prise en main de Javascript

1.1 - Javascript, un langage de programmation

  • Écrivez un programme qui calcule la factorielle d'un entier positif n demandé à l'utilisateur. Affichez le résultat dans la console du navigateur.
    • console.log("blah blah blah") imprime la chaine de caractère dans la console (n'oubliez pas de l'ouvrir !)
    • prompt("Une question") lance un pop-up permettant à l'utilisateur de rentrer une chaine de caractère
    • Le résultat est une chaine de caractère...
    • Vous pouvez utiliser parseInt("1234") pour interpréter une chaine de caractères en un entier.
  • Si ce n'est pas deja fait, encapsulez le programme de la partie précédente en une fonction fact qui calcule et retourne la factorielle d'un entier positif n (donc en utilisant le mot-cle function, comme suit).
function fact(n) {

   // code de la fonction

}
  • Écrire une fonction applique qui prend en argument un tableau tab et une fonction f, et qui a le comportement suivant:
applique(f,[1,2,3]);
// retourne le tableau [f(1),f(2),f(3)]

Votre fonction pourrait ressembler à cela:

function applique(f, tab) {

   // code de la fonction

}
  • Utilisez les fonctions fact et applique pour faire la factorielle de tous les éléments d'un tableau:
applique(fact,[1,2,3,4,5,6]);
  • Utilisez applique à une fonction non-nommée:
applique(function(n) { return (n+1); } , [1,2,3,4,5,6]);

Essayez le code et assurez-vous de comprendre ce qui se passe.

1.2 - JavaScript dans une page web

Nous allons maintenant utiliser JavaScript pour interragir avec la page web. Nous allons donc changer de fichier javascript.

1.2.1 - Premiers pas

  • Dans la page HTML (index.html), changer la source de la balise de script pour script.js
  • Ajoutez un titre et un paragraphe à la page HTML. Par exemple, mettez dans le <body>:
    <h1>Bienvenue sur ma page !</h1>
    <p>Voici un paragraphe.</p>
  • Ajouter dans le fichier de script script.js un appel console.log("le code javascript s'exécute"); et assurez vous que lorsque la page se charge cela apparait dans la console.

1.2.2 - Interception d'un événement

Dans votre page web "index.html", déclenchez l'affichage de la phrase "Le paragraphe a été cliqué" dans la console web lorsque l'utilisateur clique sur le paragraphe.

  • Utilisez l'attribut onclick qui capture l'évènement click...
  • Ensuite, essayez de le faire en javascript pur.

1.2.3 - Accès au document

Afficher dans la console web l'objet document de la page web "index.html". Pour que ce soit intéressant, ouvrez bien la page index.html de votre repl dans une fenêtre dédiée, et ouvrez la console javascript de firefox.

Attention

On ne peut manipuler le contenu d'une page que lorsque cette page est entièrement chargée, ce qui n'est pas le cas lorsque le JavaScript commence à s'exécuter si la balise script est en début de page. En revanche la fonction désignée par onload est justement appelée lorsque le chargement de la page est terminé... Ou il faut bien mettre la balise de script à la fin.

1.3 - Utiliser DOM avec JavaScript

Clonez maintenant le repl https://repl.it/@CSappliWeb2022/Javascript2#index.html

1.3.1 - Accès au contenu du document

Afficher dans la console web la valeur de l'élément <title> de la page web "index.html".

  • Vous pouvez aller le chercher "à la main" en parcourant l'arbre
  • Ou directement avec getElementsByTagName (sachant que cette fonction renvoie une collection)

1.3.2 - Rechercher des éléments dans la page

Ecrire un programme JavaScript qui affiche dans la console web les attributs href des balises <a> présentes.

  • getElementsByTagName est encore votre ami.

1.3.3 - Modifions la page !

A. Dans votre programme, changer le style des liens: mettez-les en gras, ou en couleur...

  • Utilisez par exemple setAttribute pour définir l'attribut style des éléments qui vous intéressent.
  • N'oubliez pas les points-virgules à la fin. Par exemple, voila un style potentiel:
"color: green; font-weight: bold;"

B. Modifiez le contenu des balise <a>: ajouter une image (par exemple Une belle image de lien - Copiez l'adresse du lien pour l'utiliser) en fin de texte.

  • Pour chaque élément a:
    • créez d'abord un nouvel élément tout neuf de type img
    • mettez-lui le bon attribut src avec la fonction setAttribute
    • ajoutez-le à la fin des fils de la balise a avec appendChild
  • Attention: créez bien un nouvel élément img pour chaque balise a ! Si vous ne comprenez pas pourquoi, demandez-moi.

2 - Montrer et cacher des éléments.

Dans cet exercice, nous allons compléter l'application météo commencée mardi. L'objectif à terme est d'avoir les valeurs que vous avez entrées à la main directement envoyées par le serveur.

Si vous pensez que votre page n'ira pas pour l'exercice

vous trouverez en bas de cette page un exemple de fichier HTML. Vous pouvez bien sûr l'incorporer à votre page le cas échéant.

À faire

Dans un premier temps, et c'est l'objectif de cet exercice, nous allons simplement les faire modifier par des fonctions javascript en local.

  • Tout d'abord, les valeurs (températures, pression, etc) que vous avec entrées à la main ne sont peut-être pas facilement accessible avec javascript. Nous allons commencer par éditer le fichier html pour cela.
    • Encadrez chacune des valeurs qui devraient être dynamiques par une balise <span>. Donnez à ces balises la classe contenu
    • Encadrez les unités par une autre balise <span>. Donnez à ces balises la classe unite
    • Les valeurs peuvent être affichées, ou pas (c'est l'objectif de la page de configuration). Pour chaque balise qui contient la donnée et sa description, donnez la classe contenant et un identifiant unique.
    • Par exemple, si vous aviez cela:
<ul>
  <li>Température: -42 C</li>
  <li>Pression: 1 bar</li>
  <li>Vent: 100 km/h</li>
</ul>

On souhaite avoir cela:

<ul>
  <li class="contenant" id="temp">Température: <span class="contenu">-42</span> <span class="unite">C</span></li>
  <li class="contenant" id="pression">Pression: <span class="contenu">1</span> <span class="unite">bar</span></li>
  <li class="contenant" id="vent">Vent: <span class="contenu">100</span> <span class="unite">km/h</span></li>
</ul>
  • Ajoutez dans l'entête de votre fichier html une balise <script> qui pointe sur un fichier javascript (avec l'extension .js) dans le même répertoire.
  • Le reste de l'exercice se fait en éditant ce fichier javascript.
  • Écrivez une fonction cacheTout() qui cache toutes les balises de la classe contenant (utilisez un style CSS par exemple).
  • Écrivez une fonction montre() qui prend en argument une liste de string et qui affiche les balises qui ont les identifiants en question.
  • Testez vos fonctions dans la console de votre navigateur.
    • Par exemple, si vous faites
cacheTout();

cela doit tout enlever. Puis (dans le cas de l'exemple qui précède) si vous faisiez

montre(["temp", "vent"])

cela devrait réafficher uniquement la température et le vent.

3 - Modifier l'arbre de la page à la volée.

Nous allons maintenant parametrer les unités. On se restreint ici à la température et au vent pour simplifier, mais dans un deuxième temps vous pouvez faire plus !

  • Dans votre fichier javascript, créez une table d'association comme dans l'exemple suivant:
var unitesPossibles = {
  temp : ["C", "F", "K"],
  vent : ["km/h", "m/s"],
}

Cette table va contenir les choix possibles pour les unités. Évidemment, je donne une table pour l'exemple du dessus. Faites pour votre cas de figure.

  • Ajoutez une autre table, qui contient les indices des unités choisies. Par exemple:
var unitesChoisies = {
  temp : 0,
  vent : 1
}

Donc on veut des températures en Celsius et un vent en mètres/seconde.

  • Finalement, il faut une dernière table qui contient les valeurs courantes des mesures. On prend la convention que la valeur est dans l'unité indiquée par l'indice 0 (donc ici: Celsius et km/h).
var valeursCourantes = {
  temp : -42,
  vent : 100
}
  • On veut maintenant une fonction afficher() qui prend ces différents éléments et qui change les valeurs de la page en conséquence.
    • On accède au champ d'une table d'association avec la le point ".", par exemple dans l'exemple valeurCourantes.vent vaut -42.
    • Pour passer de Celsius en Fahrenheit, la formule est TF = TC x 9/5 + 32
    • Pour passer de Celsius en Kelvin, la formule est TK = TC + 273,15
    • Pour passer de km/h en m/s... Voyons, vous devriez savoir faire.
    • Il faut donc changer les textes (avec element.innerHTML = "nouveau texte") des balises <span>, charactérisées par le nom de leur classe. Par exemple, pour changer le texte "km/h", il faut accéder à la balise <span> dans la classe "unite" qui est fille (element.children) de la balise avec l'identifiant "vent".
    • En principe, si dans la console vous modifiez les valeurs, par exemple:
valeursCourantes.temp = 23
valeursCourantes.vent = 15
unitesChoisies.vent = 0
afficher()

cela doit changer les unités et les valeurs en conséquence.

4 - AJAX

Dans la l'exercice précédant, vous avez stocké les les valeurs des données affichées par votre application météo dans la variable javascript valeursCourantes locale à la page. Les unités à utiliser pour l'affichage étaient stockées dans la variable unitesChoisies. La page était ensuite modifiée à l'aide d'une fonction afficher() qui utilisait cette variable.

Nous allons voir comment récupérer ces valeurs depuis des sources externes.

4.1 - Accès à des données JSON statiques avec JavaScript

  • On fournit la donnée JSON suivante, à mettre dans un fichier pref.json et qui contient les préférences qui sont sensées correspondre au fichier config.html, que nous traiterons plus tard. Il s'agit donc des valeurs à placer dans la variable globale unitesChoisies, et de l'état d'affichage des différents champs.
{
 "temp": {
    "unite": 0,
    "state": 1
    },
 "pression": {
    "unite": 0,
    "state": 0
 },
 "nuage": {
    "visib": {
       "unite": 1,
       "state": 1
    },
    "status": 1
 },
 "vent": {
    "unite": 0,
    "state": 1
 }
}

L'objectif est de récupérer ce fichier à l'aide de la fonction fetch, de changer les valeurs de la variable globale unitesChoisies et d'afficher ou de cacher ce qu'il faut.

  • Si state (ou status) vaut 0, on veut cacher le champ en question, le montrer sinon.
  • Le champ unite donne la valeur a mettre dans unitesChoisies

À faire

  • Écrivez une fonction litConfig() qui lit le fichier json avec fetch(), récupère les données, mets à jour la variable globale unitesChoisies et affiche/cache les champs ad-hoc.
  • Testez votre fonction dans la console:
    • Modifiez le fichier JSON avec un éditeur de texte
    • Lancez votre fonction
    • Affichez la variable et vérifiez que cela s'est mis à jour

4.2 - De la météo en direct.

Dans cet exercice, nous allons récupérer les informations depuis internet, en utilisant un service météo.

Dans ce repl, vous trouverez le coeur d'une interface web pour une application d'affichage de la météo utilisant les service de http://openweathermap.org. Nous allons essentiellement nous inspirer de ce repl pour compléter notre application météo. Vous pouvez évidemment simplement importer dans votre application météo les morceaux intéressants.

L'objectif est de réaliser une variant de la fonction litConfig() pour récupérer les données en faisant un appel à openweathermap, comme dans l'exemple.

À faire

  1. Allez sur la page http://openweathermap.org/appid et obtenez votre propre ID.
  2. Changez l'ID pour la votre dans le repl exemple. Assurez-vous que cela marche toujours. Note: l'ID peut mettre un certain temps avant d'être utilisable. Dans l'interval, vous pouvez utiliser celui de l'exemple.
  3. Changez la langue utilisée pour les réponses: par exemple, en français. La doc du service est là.
  4. Le serveur donne beaucoup d'informations: Pouvez-vous trouver la température, la pression, le vent, l'humidité, la couverture nuageuse ?
  5. Un champ "icon" pointe sur une icone résumant le temps : Voir la doc ici.
  6. Dans votre application météo, réalisez une fonction litDonnees() pour effectuer un appel AJAX avec fetch() à openweathermap. Quid des unités des valeurs numériques ?

4.3 - Utilisons les deux informations à la fois

L'idée maintenant est d'avoir les deux fichiers en même temps. Problème : on veut s'assurer que la variable unitesChoisies a bien été mise à jour avant d'envisager d'appeler la fonction affichage(). Mettre cote à cote les deux appels à fetch ne résoudra pas le problème: chacun des appels est asynchrone. C'est le propre de la programmation événementielle: Il faut donc forcer la séquencialité des choses.

Une manière de faire est d'utiliser la compositionalité de la méthode .then() des promesses.

À faire

Réalisez une fonction litConfigDonnee() fait en séquence

  1. la lecture de la configuration
  2. la lecture des données
  3. l'affichage

Il s'agit simplement de chainer les fonctions à l'aide de .then().

Attention

La méthode .then() prend nécéssairement une fonction en argument... Par exemple, si on définit

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

qui attend le temps demandé avant de faire le contenu du .then() qui suivrait, les deux choses suivantes n'ont pas le même comportement:

sleep(1000).then(() => console.log("1")).then(console.log("2"))

et

sleep(1000).then(() => console.log("1")).then(() => console.log("2"))

5 - Optionnel - Affichage de la météo sur une carte

Notre application météo comporte une image. Nous allons remplacer cette image par une carte où nous placeront à la bonne place l'icone donnée par openweathermap. À cette fin, nous allons utiliser un service de fond de carte. Google propose un tel service, mais je vous propose plutôt d'utiliser ici OpenLayers (nous nous servirons de la version 2, particulièrement simple à utiliser).

  1. Trouver dans les informations données par le serveur openweathermap la longitude et la latitude de la ville.
  2. Nous allons maintenant faire appel à OpenLayers pour afficher l'icone à la latitude et longitude donnée. Un exemple d'utilisation de MapQuest (utilisant la version 2) se trouve sur ce repl. Inspirez-vous en pour ajouter une carte à votre appli météo centrée sur la ville avec l'icone résumant le temps.

Conclusion

Nous avons donc (presque) une application complète: il ne manque plus qu'à mettre en fonctionnement la page de configuration. Ce sera l'objectif du TD de jeudi, avec la mise en oeuvre d'un micro-service web.

Appendix - Exemple de page à utiliser

Si vous n'êtes pas sûr de la page principale de votre application météo, ou si vous pensez qu'elle ne correspond pas à l'exercice, en voici une particulièrement simple (sans CSS) mais qui devrait faire l'affaire. Évidemment, les liens ne marcheront pas...

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Météo</title>
  </head>
  <body>
    <header>
      <h1>La météo de Gif-Sur-Yvette</h1>
    </header>
    <nav>
      <ul>
        <li><a href="config.html">Configuration</a></li>
        <li><a href="contact.html">Contact</a></li>
      </ul>
    </nav>
    <main>
      <section>
        <h2>La ville</h2>
        <p>Gif-sur-Yvette (prononcé [ ʒif syʁ ivɛt̪] ) est une commune
          française située à vingt-quatre kilomètres au sud-ouest de
          Paris dans le département de l’Essonne en région
          Île-de-France. Elle est le chef-lieu du canton de
          Gif-sur-Yvette. </p>
      </section>  
      <section>
        <h2>Une carte</h2>
        <img width="200px" src="http://www.cartes-de-france.fr/carte/carte_de_france_relief.gif">
      </section>
      <section>
        <h2>Les données actuelles</h2>
        <ul>
          <li>Température : 12 degrés</li>
          <li>Couverture nuageuse : gris foncé</li>
          <li>Vent : 40 km/h</li>
          <li>Visibilité : 5 mètres </li>
          <li>Pression : 4 bars</li>
        </ul>
      </section>
    </main>
    <footer>
      <address>Gateau du Glouton Inc. Corp.</address>
    </footer>
  </body>
</html>