Skip to content
Snippets Groups Projects
Commit c29b356f authored by Thomas Fritsch's avatar Thomas Fritsch
Browse files

ajout énoncé TP5 jQuery

parent 54450cab
No related branches found
No related tags found
No related merge requests found
<img src="images/readme/header-small.jpg" >
# A. Préparatifs <!-- omit in toc -->
Vous commencez maintenant à avoir l'habitude, je ne rentrerais donc pas dans les détails mais voici les différentes étapes pour le lancement du projet en mode [TL;DR](https://en.wiktionary.org/wiki/tl;dr)
1. **Créez un fork de ce TP sur https://gitlab.univ-lille.fr/js/tp5/-/forks/new**
- Choisissez de placer le fork dans votre profil utilisateur et placez le **en mode "privé"**
- Ajoutez en tant que "reporter" votre encadrant de TP (`@nicolas.anquetil`, `@patricia.everaere-caillier` ou `@thomas.fritsch`)
2. **Tapez dans un terminal :**
```bash
mkdir ~/tps-js
git clone https://gitlab.univ-lille.fr/<votre-username>/tp5.git ~/tps-js/tp5
codium ~/tps-js/tp5
```
3. **Puis dans 3 terminaux splittés de VSCodium :**
```bash
npm i && npm run watch
```
> _**NB :** `npm i ...` est un **raccourci** pour `npm install ...` et l'opérateur `&&` permet de chaîner 2 commandes_
```bash
java -jar pizzaland-jar-with-dependencies.jar
```
> _**NB :** à lancer dans le dossier où vous aviez téléchargé le serveur REST [lors du précédent TP](https://gitlab.univ-lille.fr/js/tp4/-/blob/master/B-ajax.md#b3-appeler-une-api-restjson-en-get)_
puis enfin :
```bash
npx serve -s -l 8000
```
> _**NB :** **si vous souhaitez plus de précisions** sur les commandes précédentes et l'installation / configuration du projet, vous pouvez vous référer au chapitre [A. Préparatifs](https://gitlab.univ-lille.fr/js/tp4/-/blob/master/A-preparatifs.md) du précédent TP ou simplement demander de l'aide à votre professeur_ 😄
Le résultat attendu est le suivant :
<img src="images/readme/pizzaland-00.jpg" >
## Étape suivante <!-- omit in toc -->
Si la compilation fonctionne, vous pouvez passer à l'étape suivante : [B. jQuery : les bases](B-jquery-bases.md)
\ No newline at end of file
<img src="images/readme/header-small.jpg" >
# B. JQUERY <!-- omit in toc -->
_**jQuery doit permettre de simplifier notre code, voyons dans cette partie du TP comment l'utiliser**_
## Sommaire <!-- omit in toc -->
- [B.1. Installation de jQuery](#b1-installation-de-jquery)
- [B.2. La fonction $()](#b2-la-fonction-)
- [B.3. la méthode `.html()`](#b3-la-méthode-html)
- [B.4. addClass() / removeClass()](#b4-addclass-removeclass)
- [B.5. les événements avec jQuery](#b5-les-événements-avec-jquery)
## B.1. Installation de jQuery
1. Avant d'installer jQuery, **stoppez la compilation du code et arrêtez la commande `npm run watch`** en tapant <kbd>CTRL</kbd>+<kbd>C</kbd> dans le terminal
1. **Installez maintenant la dernière version stable de jQuery** avec npm :
```bash
npm i jquery
```
> _**NB :** comme expliqué dans la partie [A. Préparatifs](./A-preparatifs.md), `npm i ...` est un raccourci pour la commande `npm install ...`_
2. **Ouvrez le fichier `package.json` et constatez que jQuery a été ajouté à la liste des dépendances du projet :**
```bash
"dependencies": {
"jquery": "^3.6.0"
}
```
> _**NB :** vous avez peut-être remarqué que contrairement aux autres packages que l'on avait installé jusque là (`babel`, `webpack`, etc.), **`jquery` a été ajouté dans la section `"dependencies"` et pas `"devDependencies"`.**_
>
> _En effet, tous les paquets que l'on a installé précédemment ne sont utilisés que pendant la **phase de développement** (pour la compilation ou le formatage de code source) mais ne contiennent rien qui soit vraiment utilisé "dans" notre code. C'est la raison pour laquelle on avait installé tous ces paquets avec **l'option `--save-dev`** (par exemple dans le TP1, on avait fait : `npm install --save-dev @babel/core @babel/cli`, vous vous souvenez ?_ :thinking: _) ce qui avait pour conséquence d'ajouter ces paquets dans les **`"devDependencies"`**._
>
> _**Pour jQuery, on n'a pas utilisé l'option `--save-dev` car on va utiliser jQuery dans notre code, de fait il est installé dans la section `"dependencies"`.**_
>
> _Documentation officielle :_
> - _dependencies : https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies_
> - _devDependencies : https://docs.npmjs.com/cli/v7/configuring-npm/package-json#devdependencies_
3. **Une fois tout installé, vous pouvez relancer la compilation à l'aide de la commande :**
```bash
npm run watch
```
Vérifiez ensuite que la page s'affiche toujours correctement dans le navigateur.
## B.2. La fonction $()
**Comme vu en cours, jquery met à disposition une fonction "magique" `$()` qui permet notamment de sélectionner des éléments HTML puis d'y appliquer des modifications.**
1. **Dans le fichier main.js, importez jQuery** :
```js
import $ from 'jquery';
```
Vous noterez que cette instruction `import` est un peu différente de celles que l'on avait faites jusque là : en effet, **quand on importe un paquet npm, on indique après le `from` juste son nom** (_et pas un chemin relatif comme pour les modules que l'on développe nous-mêmes_).
2. **Utilisez la fonction $ pour sélectionner le texte du logo et affichez le contenu dans la console** :
```js
console.log( $('.logo span') );
```
<img src="images/readme/logo-log.png" />
**La valeur retournée par `$()` est un [objet jQuery](http://api.jquery.com/Types/#jQuery).** \
Il s'agit d'un **objet** qui contient la liste de **tous** les éléments HTML correspondant au sélecteur CSS (_ici nous n'en avons qu'un seul, d'où le `length = 1`_) et des méthodes qui vont permettre de les manipuler.
## B.3. la méthode `.html()`
Comme vu en cours, la méthode [`.html()` (_documentation_)](https://api.jquery.com/html/) est plus ou moins l'équivalent de la propriété `.innerHTML` : elle permet non seulement de récupérer le contenu d'une balise mais aussi de le modifier.
La principale différence niveau syntaxe, c'est que `innerHTML` est une propriété, alors que `html` est une méthode !
Voyons comment l'utiliser :
1. **Avec la méthode `.html()` affichez le contenu texte du logo** :
```js
console.log( $('.logo span').html() );
```
<img src="images/readme/logo-log-html.png" />
2. **La méthode `.html()` peut aussi servir à modifier le contenu d'une balise : remplacez le contenu du logo par `'jQuery<em>forever</em>'` à l'aide de la méthode `.html()`**.
<img src="images/readme/logo-html.png" />
3. Comme expliqué plus haut, jQuery retourne un objet qui peut contenir **une collection d'éléments HTML**. Si le sélecteur CSS que l'on passe à la fonction `$()` correspond à plusieurs éléments de la page, les méthodes que l'on appellera ensuite sur l'objet jQuery s'appliqueront **automatiquement à tous les éléments de la page** !
**Remplacez le sélecteur CSS précédent `'.logo span'` par `'a'`** (_pour sélectionner tous les liens de la page_) :
<img src="images/readme/a-html.png" />
Voyez comment avec **une seule instruction** on a pu modifier **tous** les liens de la page (_tout du moins tous ceux qui existaient au moment où l'on a exécuté l'instruction_) : les liens du header et du footer ont tous été remplacés ! \
Pas besoin de `forEach` ou de boucle `for`, elle est inclue dans chaque méthode de l'objet jQuery retourné par `$()`.
En revanche vous voyez que les `<a href>` des vignettes de la `PizzaList` n'ont pas été transformés ! C'est normal car **au moment où notre code s'exécute** (_à la fin du `main.js`_) ces liens **n'existent pas encore dans la page**, puisque l'appel AJAX qui les génère (_le `fetch()` dans la méthode `PizzaList::mount()`_) n'est pas encore terminé !
4. **Effacez le code précédent de manière à revenir à un contenu de page normal** et passons à la suite.
## B.4. addClass() / removeClass()
On vient de voir comment modifier le contenu d'une balise, mais on n'a pas encore essayé de modifier des attributs et notamment les classes CSS d'une balise.
Pour s'y entrainer, je vous propose d'essayer d'améliorer l'expérience utilisateur (UX) de notre application, en ajoutant un feedback visuel lorsqu'un appel AJAX est en cours.
En effet, contrairement aux applis web classiques, lorsqu'un appel AJAX commence, le navigateur ne donne aucun indice permettant à l'utilisateur de deviner qu'il se passe quelque chose (la page ne s'efface pas, il n'y a pas de "loader" ou de barre de progression), il peut donc avoir l'impression que sa demande n'a pas été prise en compte, que le site est "planté", voire clicker frénétiquement sur un bouton pour le faire marcher (ce qui n'aura comme autre effet que de lancer autant de requêtes HTTP supplémentaires...).
Il faut donc donner cette information à l'utilisateur nous même, en affichant par exemple un message ou une animation dans la page. C'est ce que l'on va faire :
1. **Juste avant de lancer l'appel AJAX `GET` `/api/v1/pizzas`, ajoutez la classe CSS `is-loading` sur la balise de classe CSS `'pizzaList'` à l'aide de la méthode [`addClass()`](https://api.jquery.com/addClass/)** :
<img src="images/readme/addClass-is-loading.png">
> _**NB :** pour convertir un élément HTML (comme `PizzaList::element`) en objet jQuery, vous pouvez simplement passer cet élément à la fonction `$` comme ceci : `$(monElementDOM)` (cf. https://api.jquery.com/jQuery/#jQuery-element)._\
> _Cela aura pour effet d'"entourer" ("wrapper") l'élément DOM dans un objet jQuery, qui disposera lui de toutes les méthodes qu'on a vu jusque là et notamment de la méthode `addClass()` !_
6. **Une fois l'appel AJAX terminé et la liste des pizzas récupérées, retirez la classe CSS 'is-loading' de la balise 'pizzaList'** à l'aide de la méthode [`removeClass()`](https://api.jquery.com/removeClass/) :
Comme notre serveur REST se trouve sur notre machine, le délai de chargement est très rapide, on a donc à peine le temps de voir le loader apparaître.
La solution est d'utiliser le système de bridage de bande passante intégré aux devtools du navigateur. Par exemple sur Chrome :
<img src="images/readme/chrome-throttle.jpg">
Sélectionnez `Fast 3G` ou `Slow 3G` et rechargez la page pour voir l'impact sur l'affichage.
## B.5. les événements avec jQuery
Dernier point à connaître pour pouvoir travailler avec jQuery, la gestion des événements.
En effet, jQuery propose un équivalent de la méthode `.addEventListener()` qui est la méthode [`.on()` (_documentation_)](https://api.jquery.com/on).
> _**Attention :** vous trouverez sans doute sur les Internets, des sites, tutos, posts stackoverflow, etc. qui parlent de méthodes `.click()`, `.hover()`, `.submit()`. Ce sont des méthodes qui servaient de "raccourcis" pour ajouter des écouteurs d'événement plus rapidement qu'avec `.on()`._
>
> _Hors depuis la version 3.3.0 sortie début 2018, ces méthodes sont considérées comme **dépréciées**. L'information n'est pas indiquée de manière claire ni sur la doc de ces méthodes (https://api.jquery.com/click) ni dans la liste des features deprecated (https://api.jquery.com/category/deprecated/deprecated-3.3/) mais c'est précisé sur le blog de jQuery : https://api.jquery.com/category/deprecated/deprecated-3.3/ et dans les issues de leur repo github : https://github.com/jquery/jquery/issues/3214_
1. **A l'aide de la méthode [`.on()`](https://api.jquery.com/on/) détectez le clic sur le logo** et faites en sorte de rediriger l'utilisateur vers la page d'accueil du site (la `PizzaList`) lorsqu'on clique dessus.
> _**NB1 :** la fonction de callback passée à `.on()` reçoit en paramètre un objet de type [Event](http://api.jquery.com/category/events/event-object/) avec (comme dans l'API DOM) une méthode [`preventDefault()`](http://api.jquery.com/event.preventDefault/) qui permet **d'empêcher le comportement par défaut du navigateur**._
2. **Utilisez la méthode `.on()` pour détecter le clic sur chaque vignette de pizza** et affichez l'url du lien sur lequel on a cliqué dans la console.
Pour sélectionner seulement les balises qui sont contenues à l'intérieur d'un autre élément HTML (_ici par exemple, vous voulez sélectionner tous les liens qui se trouvent à l'intérieur de l'objet `this.element` de la `PizzaList`_) sachez que vous pouvez passer un 2e paramètre à la fonction `$()` : le **["context" (_cf. documentation_)](https://api.jquery.com/jQuery/#jQuery-selector-context)**. Ce "context", c'est justement un élément HTML dans lequel jQuery va restreindre sa recherche !
Par exemple, ce code :
```js
const footer = document.querySelector('body > footer');
$('div', footer).html('Breaking Bad');
```
Ne va modifier que les `<div>` qui se trouvent dans le footer, les autres ne seront pas impactées !
> _**NB :** il y a différentes façons de résoudre cet exercice, mais sachez que l'objet `event` envoyé à votre fonction de callback, dispose (en plus de `.preventDefault()`) d'une propriété [`currentTarget` (_documentation_)](http://api.jquery.com/event.currentTarget/) qui indique l'élément qui a déclenché l'événement._
## Étape suivante <!-- omit in toc -->
Maintenant que l'on a découvert comment manipuler la page avec jQuery, terminons ce TP avec le chapitre [C. jQuery or not jQuery ?](C-jquery-ou-pas.md).
\ No newline at end of file
<img src="images/readme/header-small.jpg" >
# C. jQuery or not jQuery ?<!-- omit in toc -->
_**Maintenant que l'on connaît les fonctions de base de jQuery, voyons ce que ça donne une fois tout le code de l'application converti.**_
## Sommaire <!-- omit in toc -->
- [C.1. Migrer à jQuery](#c1-migrer-à-jquery)
- [C.2. Recoder jQuery ?](#c2-recoder-jquery-)
- [C.3. Conclusion](#c3-conclusion)
## C.1. Migrer à jQuery
**Modifiez le code de l'application pour utiliser jQuery au lieu des méthodes classiques de l'API DOM (`querySelector`, `innerHTML`, `classList.add/remove`, `addEventListener`, ...).**
> _**Astuce :** Quand vous stockez un objet jQuery dans une variable, une convention de nommage courramment employée consiste à **préfixer le nom de la variable avec un `$`** (pour indiquer qu'il s'agit d'un objet jQuery à l'intérieur)._ \
> _Par exemple :_
> ```js
> const $mainMenu = $('.mainMenu');
> ```
## C.2. Recoder jQuery ?
1. Maintenant que toute votre application a été "convertie" à jQuery, pour comprendre pourquoi certains veulent se passer de jQuery, je vous invite à comparer le poids du bundle de votre application (`build/app.bundle.js`) avec le poids de celui du TP précédent. \
**Que constatez vous ?**
2. **Recompilez les deux TPs en mode "production"** (`npm run build`). Constatez vous la même chose ?
4. Si le coeur vous en dit et s'il vous reste de l'énergie, **je vous propose de désinstaller `jQuery` (`npm uninstall jquery`) et de coder votre propre fonction `$()`**. Créez-vous un module à part (par exemple dans `src/lib/jqlite.js`) et codez y la fonction `$()` (_n'oubliez pas de l'exporter !_).
Cette fonction `$()` devra fonctionner **exactement** comme celle de jQuery, c'est à dire que vous ne devez pas avoir à toucher au reste du code de votre appli (_sauf pour changer les `import`_).
Vous pouvez vous aider de http://youmightnotneedjquery.com/ mais gardez à l'esprit que leurs exemples ne gèrent pas le fait qu'un sélecteur peut correspondre à plusieurs balises et qu'il faut parfois "boucler" sur plusieurs éléments !
Une fois tout ça terminé, comparez le poids du bundle ainsi généré avec celui de jquery.
## C.3. Conclusion
**Si vous avez terminé ce TP vous pouvez être fiers de vous 👏 !**\
**Gardez quand même à l'esprit que jQuery gère tout un tas de choses que vous n'avez pas codées :**
- une grande quantité de fonctionnalité qu'on a pas utilisé dans pizzaland : les animations, les méthodes show/hide, ajax, etc., etc.
- une multitude de cas limites que vous ne gérez pas dans votre code
- et surtout qu'elle garanti que son code fonctionne sur [énormément de navigateurs](https://jquery.com/browser-support/) (ce qui n'est probablement pas le cas de notre lib "maison" :wink: ).
# TP5 - jQuery
<img src="images/readme/header.jpg">
## Objectifs
- Utiliser les fonctions de base de jQuery
- Comprendre comment fonctionne jQuery et quand l'utiliser
## Sommaire
Pour plus de clarté, les instructions du TP se trouvent dans des fichiers distincts (un fichier par sujet), procédez dans l'ordre sinon ça fonctionnera beaucoup moins bien ! :
1. [A. Préparatifs](A-preparatifs.md)
2. [B. jQuery : les bases](B-jquery-bases.md)
3. [C. jQuery or not jQuery ?](C-jquery-ou-pas.md)
\ No newline at end of file
......@@ -42,17 +42,18 @@ body > header > nav {
}
body > header .logo {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
color: #eee;
text-decoration: none;
font-family: GravitasOne;
}
body > header .logo img {
width: 120px;
}
body > header .logo span {
font-family: GravitasOne;
text-transform: uppercase;
font-size: 22px;
letter-spacing: 4px;
......
......@@ -97,6 +97,7 @@ a:hover {
right:0;
bottom:0;
z-index: 1;
background-color: white;
}
.is-loading:after {
content: ' ';
......
images/readme/a-html.png

3.27 MiB

images/readme/addClass-is-loading.png

194 KiB

images/readme/chrome-throttle.jpg

148 KiB

images/readme/header-small.jpg

21.6 KiB

images/readme/header.jpg

42.2 KiB

images/readme/logo-html.png

180 KiB

images/readme/logo-log-html.png

27.9 KiB

images/readme/logo-log.png

219 KiB

images/readme/pizzaland-00.jpg

762 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment