---
puppeteer:
    format: A4
    margin:
        top: "0.5cm"
        left: "0.5cm"
        right: "0.5cm"
        bottom: "0.5cm"
---

# Web - Activité 3 - Carrousel de photos en JavaScript

## Objectifs

- Développement d'un carrousel de photos en JavaScript.
- Le carrousel a pour but de faire défiler des photos de bâtiments pour le site de l'entreprise Numérobis afin de montrer aux visiteurs les projets sur lesquels ils ont pu travailler.

## Évaluation

- C04: ANALYSER UN SYSTÈME INFORMATIQUE
  - La structure de la solution technique est analysée avec un regard critique
  - Le calme est conservé de façon constante dans des situations particulières, tout en persévérant dans la tâche jusqu’à l’atteinte du résultat sans se décourager
- C08: CODER
  - Le code est versionné, commenté et le logiciel est documenté
  - Les composants logiciels individuels sont développés conformément aux spécifications du cahier des charges, des bonnes pratiques et des différentes politiques de sécurité et de protection des données personnelles
  - La solution (logicielle et matérielle) est intégrée et testée conformément aux spécifications du cahier des charges, des bonnes pratiques et des différentes politiques de sécurité et de protection des données personnelles
  - La résolution d'un problème nouveau imprévu est réussie en utilisant ses propres moyens conformément aux règles de la fonction

## Validation
### Livrables

- Le code final
- Le compte-rendu de l'activité

Le tout dans un répertoire avec le nom suivant `YYYY-MM-DD_TPX_NOM1_Prenom1_NOM2_Prenom2`.
- Le compte rendu peut-être livré au format Markdown ou PDF. 
- Si vous choisissez le Markdown, attention à bien livrer les éventuelles images et à la validité des liens

### Résultats attendus

- [ ] Le carrousel est intégré à la page d'accueil du site et fonctionnel.
- [ ] Le carrousel permet de faire défiler 4 photos en boucle grâce à deux boutons
- [ ] Le carrousel fait défiler automatiquement les photos toutes les 2 secondes
- [ ] Le résultat général (hors animation) ressemble à l'image ci-dessous (options incluses)
- [ ] (Optionnel) L'aspect visuel du site a été modernisé
- [ ] (Optionnel) Le carrousel affiche en haut à gauche le numéro de la photo, et en en dessous au centre son titre
- [ ] (Optionnel) Le carrousel permet d'afficher une photo au choix en cliquant sur un indicateur en bas de la photo

![final](images/final.png)

## Ressources fournies

- Documentations :
  - [HTML](https://developer.mozilla.org/fr/docs/Web/HTML)
  - [CSS](https://developer.mozilla.org/fr/docs/Web/CSS)
  - [JavaScript](https://developer.mozilla.org/fr/docs/Web/JavaScript)
- Cours de JavaScript
- 4 images à intégrer dans le projet

## Étapes

### 0 - Préparation

**Objectif :** Préparer le projet 

!!! example À faire
    - Repartir du résultat de l'activité précédente (ouvrir le répertoire dans VSCode)
    - Vérifier que votre travail a été correctement commité et qu'il ne reste plus de fichiers modifiés mais non commités
    - Créer un nouveau fichier de compte rendu vierge
    - Lancer un serveur web de développement (`python -m http.server`)
    - Ouvrir un navigateur (Firefox de préférence) et se rendre sur [cette page](https://localhost:8000)


### 1 - Découverte du JS

**Objectif :** Découvrir le JavaScript et afficher du texte dans la console

!!! example À faire
    - Dans votre navigateur, ouvrir la console JS (<kbd>F12</kbd> en général, ou voir cours)
    - Démarrer le mode "multiligne" (firefox uniquement), qui permet d'écrire plusieurs lignes de code et les exécuter
        ![multiligne0](./image.png)

        ![multiligne1](./image-1.png)
    - Écrire dans la console le code suivant, puis l'exécuter :
        ```js
        console.log("Hello, World!");
        ```
    - Vérfier que le texte `Hello, World!` s'affiche.
    - Écrire dans votre console le code suivant puis l'exécuter :
        ```js
        function carré(number){
            return number * number;
        }

        console.log(carré(5));
        ```
    - Écrire le code suivant puis analyser la portée des variables et retirer les lignes `console.log` nécessaires pour faire fonctionner le programme
        ```js
        var maVariable = "Hello";
        function maFonction(){
            console.log("Dans la fonction");
            if (true){
                var autreVariable = "World";
                let variableInterne = "Bonjour";
                console.log(`Dans le if : autreVariable = ${autreVariable}`);
                console.log(`Dans le if : variableInterne = ${variableInterne}`);
            }
            console.log(`maVariable = ${maVariable}`);
            console.log(`Hors du if : autreVariable = ${autreVariable}`);
            console.log(`Hors du if : variableInterne = ${variableInterne}`);
        }

        maFonction();
        console.log("Hors de la fonction");
        console.log(`maVariable = ${maVariable}`);
        console.log(`autreVariable = ${autreVariable}`);
        console.log(`variableInterne = ${variableInterne}`);
        ```


!!! note Dans le compte rendu
    - Quel est le rôle du javascript dans le navigateur ?
    - Que fait la fonction `console.log` ?
    - Qu'est-ce que `carré` dans le code que vous avez écrit ?
    - Comment déclarer et appeler une fonction en JavaScript ?
    - Comment déclarer une variable en JavaScript ?
    - Quelle est la différence entre var et let ?


### 2 - Choisir quelle image afficher

**Objectif :**  Écrire une fonction en Javascript pour choisir l'image à afficher

!!! example À faire
    - Récupérer les 4 images fournies dans un répertoire image dans votre projet
    - Supprimer l'image `home.png` (elle est remplacée par `building_1.png`)
    - Dans le fichier `index.html`, remplacer la balise image qui affichait `home.png` par le code suivant :
        ```html
        <div class="slideshow">
            <div class="slide">
              <img src="images/building_1.png" alt="Projet démo 1">
            </div>
        </div>
        ```
    - Vérifier que tout s'affiche correctement (comme avant). Corriger le CSS si nécessaire.
    - Rajouter les trois autres images dans la div `slideshow`, encapsulées chacune dans une balise `div` de classe `slide`. Vous devriez avoir les 4 images à la suite.
    - Modifier le CSS pour que les éléments de classe `slide` [ne soient plus affichés](https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/display)
    - Créer le fichier `slideshow.js` à la racine de votre projet
    - Ajouter la balise suivante, entre les sections `head` et `body` de votre `index.html` :
        ```html
        <script src="slideshow.js" defer></script>
        ```
    - Écrire une fonction `showSlide` qui prend un paramètre `index` représentant le numéro (de 0 à 3 ) de la photo à afficher.
        - Récupérer les 4 conteneurs de classe `slide` dans une variable locale `slides`. [Fonction à utiliser.](https://developer.mozilla.org/fr/docs/Web/API/Document/getElementsByClassName)
        - Grâce au code suivant, modifier la propriété de style `display` de l'image d'indice `index` :
            ```js
            slides[index].style.display = "block";
            ```
    - Modifier votre script `slideshow.js` pour appeler votre nouvelle fonction et affciher l'image d'indice 0
    - Compléter votre compte rendu, puis faire un commit.


!!! note Dans le compte rendu
    - A quoi servent les div de classe `slideshow` et `slide` ?
    - Pourquoi cache-t-on les images ?
    - Que fait la ligne `<script src="slideshow.js" defer></script>` ?
    - Que fait la fonction `document.getElementByClassName` ?
    - Que fait la ligne suivante : `slides[index].style.display = "block";` ? Aidez-vous du debugger CSS de votre navigateur
    - Expliquez dans les grandes lignes ce que vous avez fait à cette étape. Quelle sera, selon vous, la suite des opérations ?


### 3 - Afficher une seule image à la fois

**Objectif :** Réussir à n'afficher qu'une image

!!! example À faire
    - Modifier votre script pour effectuer des appels successifs à `showSlide` avec différentes images. Que constatez-vous ?
    - Il va donc falloir cacher les autres images pour n'afficher que celle qui nous intéresse (celle passée en paramètre). 
      Pour cela, nous allons utiliser une boucle qui va remettre la propriété `display` de chaque `slide` à `none`. Modifiez 
      votre code pour faire ça.
    - Vérifiez que cela fonctionne en faisant plusieurs appels successifs à `showSlide` (dans le même script). Seule la dernière image doit être visible.
    - Complétez le compte rendu et faites un nouveau commit.


!!! note Dans le compte rendu
    Expliquez ce que vous avez fait à cette étape.


### 4 - Changer d'image dynamiquement

**Objectif :** Ajouter des boutons pour passer à l'image suivante dynamiquement

!!! example À faire
    - Dans votre `index.html`, dans la `div` `slideshow` ajouter deux éléments `button`:
        - Le texte sera `&#10094;` qui représente &#10094;, et l'id `prevSlide`
        - Le texte sera `&#10095;` qui représente &#10095;, et l'id `nextSlide`
    - Ajoutez dans votre script la fonction : `showSlideRelative(relativeIndex)`. 
      - Elle fera appel à `showSlide`. 
      - Son paramètre `relativeIndex` indiquera de combien de slides il faudra faire tourner le carrousel.
        - Par exemple, si on passe 1 il faudra prendre l'image avec l'index suivant. Si on passe -1 il faudra prendre l'image précédente.
    - Dans votre `index.html` il va falloir dire à vos deux boutons (`prevSlide` et `nextSlide`) d'appeler la fonction que vous venez de créer lorsqu'on clique dessus. Ajoutez l'attribut `onclick` avec un appel à `showSlideRelative` (en passant 1 pour suivant et -1 pour précédent).
    - Vérifiez que votre code fonctionne. 
    - (À ajouter dans le compte rendu :) Si vous cliquez sur suivant alors que vous êtes sur l'image 4, que se passe-t-il ? Idem avec précédent à l'image 1 ? Pourquoi ? 
    - Corrigez votre algorithme.
    - Mettez à jour le compte rendu et faites un nouveau commit.


!!! question Aide
    - Vous aurez besoin de garder en mémoire la slide affichée pour savoir quelle est la slide suivante/précédente.
        - Vous aurez donc besoin d'une variable globale `slideIndex` pour retenir la slide affichée. Vous pourrez ensuite ajouter `relativeIndex` à `slideIndex`.
    - Votre nouvelle fonction doit calculer le nouvel indice et le passer à `showSlide`. 
    - N'oubliez pas de mettre à jour l'index global en mémoire dans `showSlide`


!!! note Dans le compte rendu
    - Que sont `&#10094;` et `&#10095;` ?
    - Expliquez ce que vous avez fait à cette étape. N'oubliez pas d'expliquer comment vous avez résolu le problème de rotation des indices.


### 5 - Améliorer la lisibilité des boutons de rotation

**Objectif :** Modifier le CSS des boutons pour les afficher sur l'image et aider l'utilisateurice à comprendre qu'ils sont cliquables

!!! example À faire
    - Dans le CSS, ajouter un sélecteur sur les deux boutons (via leurs ids) puis :
        - Modifier la [propriété `position`](https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/position) à la valeur `absolute` (attention à ce que vous deux boutons soient bien inclus dans la div `slideshow`)
        - Modifier la proriété `position` de `slideshow` à `relative`
        - Modifier les propriétés [`top`](https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/top) et [`right`](https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/right) des boutons de façon à ce qu'ils soient bien de chaque côté de l'image et centrés verticalement.
        - Modifier la propriété `cursor` pour changer le curseur de façon à comprendre qu'on a un objet cliquable
    - Compléter le copmpte-rendu et faire un nouveau commit


!!! note Dans le compte rendu
    - Que fait la propriété CSS `position` ? 
    - Pourquoi avons-nous changé cette propriété dans `slideshow` et les boutons ?
    - Que font `top`, `right` et `left` ? Dans quel cas peut-on les utiliser ?
    - Expliquer ce que vous avez fait à cette étape


### 6 - Effectuer une rotation automatique des images

**Objectif :** Changer l'image affichée toutes les deux secondes automatiquement

!!! example À faire
    - Lire la documentation de la fonction [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval) et celle de [`setTimeout`](https://developer.mozilla.org/fr/docs/Web/API/Window/setTimeout). Choisir celle qui vous paraît la plus adaptée. Justifier dans le compte rendu.
    - Quelle fonction (déjà écrite par vos soins) allez-vous appeler à intervalles réguliers pour faire tourner votre carrousel ? Écrivez le code correspondant. Utilisez l'aide ci-dessous.
    - Validez que votre code fonctionne.
    - Mettez à jour votre compte rendu et faites un commit.


!!! question Aide
    - Nous devons passer une **fonction** en paramètre à `setInterval` (ou `setTimeout`). Il s'agit bien d'une fonction, et non pas d'un appel à la fonction (qui ne donnerait qu'un seul résultat) ! C'est `setInterval` qui se chargera d'appeler votre fonction à intervalles réguliers.
    - Mais pourtant, nous avons besoin de passer des paramètres à cette fonction. Il faut donc une solution pour les passer également.
    - Deux possibilités pour ça :
        - `setInterval` propose de prendre des arguments supplémentaires, qui seront passés à la fonction (voir doc)
        - on utilise une fonction anonyme qui ne prend pas de paramètre avec la syntaxe suivante :
        ```js
        () => mafonction(param1, param2, ...)
        ```


!!! note Dans le compte rendu
    - Justifier le choix entre `setInterval` et `setTimeout` et expliquer l'algorithme que vous avez choisi pour changer d'image toutes les deux secondes.
    - `setInterval` et `setTimeout` permettent de passer une fonction ou un bout de code sous forme de chaîne de caractères comme paramètre. Qu'allez vous préférer et pourquoi ? Pensez à vérifier d'éventuelles problématiques de sécurité.


### 7 - Segmenter le JavaScript et le HTML

**Objectif :** Lier les actions des boutons directement depuis le CSS pour une meilleure maintenabilité du code

!!! example À faire
    Les boutons `prevSlide` et `nextSlide` ont un attribut `onclick` dans lequel vous avez ajouté du code JavaScript (`onclick=showSlideRelative(1)` par exemple).
    C'est une pratique à éviter, car elle complique la maintenance du code, et rompt la séparation des responsabilités. Le HTML ne devrait contenir que des informations de contenu.

    Nous allons donc y remédier.
    - Retirez les attributs `onclick` de vos deux boutons `prevSlide` et `nextSlide`.
    - Ajoutez une fonction d'initialisation dans votre code JS qui ne prend aucun argument.  Vous pouvez l'appeler `initcarrousel` par exemple.
    - Dans cette fonction déplacez l'appel à `showSlide(slideIndex)` que vous utilisiez pour initialiser le carrousel. Gardez en revanche la variable globale à l'extérieur.
    - Il va maintenant falloir dire aux deux boutons d'écouter les évènements de clic et de les gérer avec la méthode `showSlideRelative` :
        - Récupérez les références aux boutons par id grâce à [`document.getElementById`](https://developer.mozilla.org/fr/docs/Web/API/Document/getElementById)
        - Utilisez la méthode [`addEventListener`](https://developer.mozilla.org/fr/docs/Web/API/EventTarget/addEventListener) pour ajouter le gestionnaire d'évènements sur un clic
            - Le type sera `"click"`
            - Le listener sera une [fonction anonyme](https://developer.mozilla.org/fr/docs/Glossary/Function#diff%C3%A9rents_types_de_fonctions) qui appellera `showSlideRelative(1)` ou `showSlideRelative(-1)` selon le bouton (voir aide ci-dessous).
    - Validez que votre code fonctionne toujours comme avant.
    - Complétez le compte-rendu et faites un nouveau commit.


!!! question Aide
    Pour ajouter un gestionnaire d'évènements il faut passer un fonction **sans paramètre** à `addEventListener`. Or notre fonction `showSlideRelative` prend un paramètre (1 ou -1).
    Il faut donc trouver une solution pour passer le paramètre directement lors de la création du gestionnaire d'évènements. 

    Pour cela on peut créer une nouvelle fonction à usage unique, qui ne prendra aucun paramètre et appellera directement `showSlideRelative(1)` ou `showSlideRelative(-1)` selon le bouton.

    Ce type de fonctions à usage unique est très courant en JavaScript. C'est pour cela qu'il y a une syntaxe permettant de créer des fonctions anonymes (sans nom) qu'on peut utiliser dans ce cas.

    Deux syntaxes équivalentes sont possibles : 
    - La syntaxe classique d'une déclaration de fonction, sans nom : `(function () { /* mon code ici */ });`
    - La syntaxe fléchée, plus courte : `() => { /* Mon code ici */ };`


!!! note Dans le compte rendu
    - Expliquez ce que vous avez changé dans cette partie, et pourquoi.


## Pour aller plus loin

### Bonus - Améliorer le style du carrousel pour qu'il ressemble à celui de l'image du résultat attendu

**Objectif :** Compléter le HTML et le CSS pour avoir des boutons et un compteur d'images dans le style de l'image du résultat attendu du sujet.


### Bonus - Sélectionner l'image à afficher avec un clic

**Objectif :** Ajouter des boutons pour sélectionner une image précise sans cliquer plusieurs fois sur les boutons de rotation

!!! example À faire
    Sur l'image résultat attendu, vous avez pu voir en bas de l'image, au centre des points gris et un blanc.

    Dans cette partie, vous allez réutiliser toutes les notions vues au cours de l'activité pour dessiner cette zone et rendre les points cliquables.
    - HTML : il faudra ajouter un `div` de classe `dots` contenant des `span` de classe `dot` à la fin de la div `slideshow`. Le &#9679; est le caractère unicode `&#9679;`
    - CSS : il faut mettre la position de `dots` en relative et jouer avec `top` et `right` pour bien positionner la boîte. Améliorez le style pour un aspect visuel ressemblant à celui de l'image.
    - CSS : prévoyez une classe `active` pour le point correspondant à l'image en cours. Cela changera juste la couleur du bouton en blanc. On ajoutera cette classe au bon élément via le JS.
    - JS : Modifiez `slideShow` pour ajouter la classe `active` au point d'indice `index` (et la retirer des autres). Vous utiliserez la [propriété `className`](https://developer.mozilla.org/fr/docs/Web/API/Element/className)
    - JS : Modifiez votre initialisation pour ajouter des gestionnaires d'évènements aux points (utilisez une boucle). Il faudra appeler `showSlide(i)` dans le gestionnaire, avec i l'indice du point (correspondant à l'indice de l'image)

