forked from monnerat/web_2024
dom
This commit is contained in:
parent
d209ab2e82
commit
9f0c67b9f0
R4.01_R4.A.10
@ -3,4 +3,7 @@
|
|||||||
#### Semaine 1
|
#### Semaine 1
|
||||||
[Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
|
[Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
|
||||||
|
|
||||||
|
#### Semaine 2
|
||||||
|
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2)
|
||||||
|
|
||||||
|
|
||||||
|
BIN
R4.01_R4.A.10/cours/dom.pdf
Normal file
BIN
R4.01_R4.A.10/cours/dom.pdf
Normal file
Binary file not shown.
211
R4.01_R4.A.10/td_tp/tp2/README.md
Normal file
211
R4.01_R4.A.10/td_tp/tp2/README.md
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# TP javascript : DOM
|
||||||
|
|
||||||
|
Un peu [d'aide](./aide.md).
|
||||||
|
#### Ex1 : manipulation du dom
|
||||||
|
|
||||||
|
Vous devez compléter les [sources](./src/ex1) d'un jeu qui consiste à essayer
|
||||||
|
d'éteindre toutes les lumières d'une grille en utilisant la règle suivante.
|
||||||
|
Quand on allume/éteint une lumière, on allume/éteint aussi ses voisines.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/lights.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Chaque lumière, via l'interface dataset, possède un numéro (de 0 à size^2-1).
|
||||||
|
#### Ex2 : une vue html à partir de données js
|
||||||
|
|
||||||
|
Le fichier [data.js](./src/ex2/data.js) déclare un tableau contenant le classement de la ligue de football, récupéré depuis l'api [the sport db](https://www.thesportsdb.com/).
|
||||||
|
|
||||||
|
```js
|
||||||
|
let data = [
|
||||||
|
{
|
||||||
|
"idStanding":"2282066",
|
||||||
|
"intRank":"1",
|
||||||
|
"idTeam":"133714",
|
||||||
|
"strTeam":"Paris SG",
|
||||||
|
"strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/rwqrrq1473504808.png/tiny",
|
||||||
|
"idLeague":"4334",
|
||||||
|
"strLeague":"French Ligue 1",
|
||||||
|
"strSeason":"2022-2023",
|
||||||
|
"strForm":"WLWWW",
|
||||||
|
"strDescription":"Promotion - Champions League (Group Stage)",
|
||||||
|
"intPlayed":"18",
|
||||||
|
"intWin":"15",
|
||||||
|
"intLoss":"1",
|
||||||
|
"intDraw":"2",
|
||||||
|
"intGoalsFor":"48",
|
||||||
|
"intGoalsAgainst":"13",
|
||||||
|
"intGoalDifference":"35",
|
||||||
|
"intPoints":"47",
|
||||||
|
"dateUpdated":"2023-01-12 23:01:12"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque entrée est une objet json contenant différents champs.
|
||||||
|
|
||||||
|
Le but de l'exercice est de construire une vue de ces données sous forme d'une table html.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/ligue1.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Un [squelette](./src/ex2/index.html)
|
||||||
|
|
||||||
|
- la colonne du rang sera triable.
|
||||||
|
- un champ de recherche permettra de filtrer les lignes du classement selon le nom d'équipe.
|
||||||
|
|
||||||
|
|
||||||
|
> - Il faut itérer le tableau data, construire et ajouter au `tbody` de la table les lignes nécessaires.
|
||||||
|
> - Ajouter ensuite le traitement évenementielle pour le tri de la première colonne. On pourra effacer et recréer la vue.
|
||||||
|
> - Ajouter la possibilité de recherche à partir de champ de recherche.
|
||||||
|
|
||||||
|
#### Ex3 : modele MVC
|
||||||
|
Le but est d'écrire une todolist en javascript, en respectant le pattern MVC. Il est important de mener à bout cet exercice, car il nous servira
|
||||||
|
de fil rouge notamment en ajoutant une api rest et ajax pour la communication avec le service.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/todo.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Le contrôleur a accès à la vue et au modèle.
|
||||||
|
|
||||||
|
##### Le modèle
|
||||||
|
Cette "classe" (rien à compléter) utilise l'objet [localStorage](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage) pour sauvegarder la todolist.
|
||||||
|
Chaque todo est un objet json de la forme
|
||||||
|
|
||||||
|
```js
|
||||||
|
{ id : 1 , text : "apprendre le js", done : false }
|
||||||
|
```
|
||||||
|
|
||||||
|
La liste des méthodes publiques de cette classe
|
||||||
|
|
||||||
|
```js
|
||||||
|
/** @brief return the todolist
|
||||||
|
* @param filter "all" or "active" or "done"
|
||||||
|
* @return array of todos
|
||||||
|
*/
|
||||||
|
getTodos(filter)
|
||||||
|
|
||||||
|
/** @brief add (and save) a new todo with todoText
|
||||||
|
* @param todoText : text of the new todo
|
||||||
|
* @return the new todo
|
||||||
|
*/
|
||||||
|
add(todoText)
|
||||||
|
|
||||||
|
/** @brief delete a todo
|
||||||
|
* @param id id of the todo
|
||||||
|
*/
|
||||||
|
delete(id)
|
||||||
|
|
||||||
|
/** @brief update a todo
|
||||||
|
* @param id id of the todo
|
||||||
|
*/
|
||||||
|
edit(id,updatedText)
|
||||||
|
|
||||||
|
/** @brief toggle a todo (done<->active)
|
||||||
|
* @param id id of the todo
|
||||||
|
* @param updatedText text of the todo
|
||||||
|
*/
|
||||||
|
toggle(id)
|
||||||
|
```
|
||||||
|
|
||||||
|
##### La vue
|
||||||
|
Cette "classe" permet au contrôleur de gérer la vue.
|
||||||
|
|
||||||
|
|
||||||
|
Liste des méthodes publiques
|
||||||
|
|
||||||
|
```js
|
||||||
|
/** @brief change the active tab (all,active or done)
|
||||||
|
* @param filter the active tab (all, active or done)
|
||||||
|
*/
|
||||||
|
setFilterTabs(filter)
|
||||||
|
|
||||||
|
/** @brief update the todo list with todos
|
||||||
|
* @param todos array of todo
|
||||||
|
*/
|
||||||
|
renderTodoList(todos)
|
||||||
|
```
|
||||||
|
|
||||||
|
Le contrôleur peut s'abonner (notification de la vue au acontrôleur) aux événements suivant :
|
||||||
|
|
||||||
|
add/delete/edit/toggle todo : avec `bind{Add|Delete|Edit|Toggle}Todo`
|
||||||
|
|
||||||
|
Ces méthodes d'abonnement prennent en paramètre une fonction du contrôleur qui est appelé par la vue pour lui notifier
|
||||||
|
une requête.
|
||||||
|
|
||||||
|
```js
|
||||||
|
/** @brief subscribe to add event
|
||||||
|
* @param handler function(text)
|
||||||
|
*/
|
||||||
|
bindAddTodo(handler)
|
||||||
|
|
||||||
|
/** @brief suscribe to delete event
|
||||||
|
* @param handler function(id)
|
||||||
|
*/
|
||||||
|
bindDeleteTodo(handler)
|
||||||
|
|
||||||
|
/** @brief suscribe to edit event
|
||||||
|
* @param handler function(id,text)
|
||||||
|
*/
|
||||||
|
bindEditTodo(handler)
|
||||||
|
|
||||||
|
/** @brief suscribe to toggle event
|
||||||
|
* @param handler function(id)
|
||||||
|
*/
|
||||||
|
bindToggleTodo(handler)
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Le contrôleur
|
||||||
|
C'est lui qui gére le routage. Il y a trois urls possibles `index.html/#/{all|active|done}` (listener sur l'évenement
|
||||||
|
dom `hashchange`.
|
||||||
|
|
||||||
|
Liste des méthodes
|
||||||
|
```js
|
||||||
|
/** @brief get the todolist from the model
|
||||||
|
* and use the view to render
|
||||||
|
*/
|
||||||
|
filterTodoList()
|
||||||
|
|
||||||
|
/** @brief binding function called by the view
|
||||||
|
* to add a new todo
|
||||||
|
* @param text text of the new todo
|
||||||
|
*/
|
||||||
|
addTodo(text)
|
||||||
|
|
||||||
|
/** @brief binding function called by the view
|
||||||
|
* to delete a todo
|
||||||
|
* @param id id of the todo
|
||||||
|
*/
|
||||||
|
deleteTodo(id)
|
||||||
|
|
||||||
|
/** @brief binding function to toggle the state
|
||||||
|
* of a todo
|
||||||
|
* @param id id of the todo
|
||||||
|
*/
|
||||||
|
toggleTodo(id)
|
||||||
|
|
||||||
|
/** @brief binding function to change the text
|
||||||
|
* of a todo
|
||||||
|
* @param id id of the todo
|
||||||
|
* @param changedText new text for the todo
|
||||||
|
*/
|
||||||
|
editTodo(id,changedText)
|
||||||
|
```
|
||||||
|
|
||||||
|
Par exemple, lorsque l'utilisateur ajoute une todo :
|
||||||
|
|
||||||
|
1. la vue est sensible à l'événement de soumission du formulaire,
|
||||||
|
2. la fonction reflexe récupére le texte saisie,
|
||||||
|
3. elle appelle la fonction avec laquelle le contrôleur s'est abonné, en lui passant le texte,
|
||||||
|
4. la fonction du contrôleur `addTodo` récupére le texte,
|
||||||
|
5. le contrôleur demande au modèle la création d'une nouvelle todo,
|
||||||
|
6. le contrôleur demande un rafraichissement de la vue.
|
||||||
|
|
||||||
|
|
||||||
|
#### Travail à faire
|
||||||
|
- Compléter la méthode `bindDeleteTodo` de la vue.
|
||||||
|
- Compléter la méthode `bindToggleTodo` de la vue.
|
||||||
|
- Compléter la méthode `bindEditTodo` de la vue.
|
132
R4.01_R4.A.10/td_tp/tp2/aide.md
Normal file
132
R4.01_R4.A.10/td_tp/tp2/aide.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#### Selection d'éléments
|
||||||
|
|
||||||
|
```js
|
||||||
|
// le premier
|
||||||
|
document.querySelector(".box")
|
||||||
|
|
||||||
|
// ou tous
|
||||||
|
document.querySelectorAll(".box")
|
||||||
|
|
||||||
|
// avec l'id
|
||||||
|
|
||||||
|
document.getElementById("toto")
|
||||||
|
|
||||||
|
// selection d'un élément dans un autre
|
||||||
|
|
||||||
|
let container = document.querySeletor('.container')
|
||||||
|
container.querySelector('.box')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Traverser le dom
|
||||||
|
|
||||||
|
```js
|
||||||
|
var box = document.querySelector(".box")
|
||||||
|
box.nextElementSibling
|
||||||
|
box.previousElementSibling
|
||||||
|
box.parentElement
|
||||||
|
box.childNodes
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Création/insertion d'éléments
|
||||||
|
|
||||||
|
```js
|
||||||
|
let p = document.createElement('p')
|
||||||
|
p.textContent="blabla"
|
||||||
|
document
|
||||||
|
.getElementById("myDiv")
|
||||||
|
.appendChild(p)
|
||||||
|
|
||||||
|
div.replaceChildren(p)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Gestionnaire évènementiels
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.querySelector(".button").addEventListener("click", (e) => { /* ... */ })
|
||||||
|
document.querySelector(".button").addEventListener("mouseenter", (e) => { /* ... */ })
|
||||||
|
document.addEventListener("keyup", (e) => { /* ... */ })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### window/document prêt
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded",() = > { .....})
|
||||||
|
// le dom a été construit (on n'attend pas le chargement du css, images, etc.
|
||||||
|
|
||||||
|
windon.addEventListener('load',()=>{...})
|
||||||
|
// le dom est prêt et toutes les ressources ont été chargées.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### dataset
|
||||||
|
```html
|
||||||
|
<div id="user" data-id="1234567890" data-user="carinaanand" data-date-of-birth>
|
||||||
|
Carina Anand
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const el = document.querySelector("#user");
|
||||||
|
|
||||||
|
// el.id === 'user'
|
||||||
|
// el.dataset.id === '1234567890'
|
||||||
|
// el.dataset.user === 'carinaanand'
|
||||||
|
// el.dataset.dateOfBirth === ''
|
||||||
|
```
|
||||||
|
#### Local storage
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
localStorage.setItem('monChat', 'Tom')
|
||||||
|
let cat = localStorage.getItem('myCat')
|
||||||
|
localStorage.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Limiter les appels successifs à une fonction
|
||||||
|
|
||||||
|
```js
|
||||||
|
function debounce(f,wait)
|
||||||
|
{
|
||||||
|
let timeout
|
||||||
|
return function(...args){
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout=setTimeout(()=>f(...args),wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Gestion du css depuis le DOM
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Select the first .box and change its text color to #000
|
||||||
|
document.querySelector(".box").style.color = "#000";
|
||||||
|
|
||||||
|
// Set color to #000 and background to red
|
||||||
|
var box = document.querySelector(".box")
|
||||||
|
box.style.color = "#000"
|
||||||
|
box.style.backgroundColor = "red"
|
||||||
|
|
||||||
|
// Set all styles at once (and override any existing styles)
|
||||||
|
box.style.cssText = "color: #000; background-color: red"
|
||||||
|
|
||||||
|
|
||||||
|
// Hide and show an element by changing "display" to block and none
|
||||||
|
document.querySelector(".box").style.display = "none"
|
||||||
|
document.querySelector(".box").style.display = "block"
|
||||||
|
|
||||||
|
// Add, remove, and the toggle the "focus" class
|
||||||
|
var box = document.querySelector(".box")
|
||||||
|
box.classList.add("focus")
|
||||||
|
box.classList.remove("focus")
|
||||||
|
box.classList.toggle("focus")
|
||||||
|
|
||||||
|
|
||||||
|
box.classList.add("focus", "highlighted")
|
||||||
|
box.classList.remove("focus", "highlighted")
|
||||||
|
|
||||||
|
box.classList.add("focus", "highlighted")
|
||||||
|
box.classList.remove("focus", "highlighted")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
BIN
R4.01_R4.A.10/td_tp/tp2/img/lights.png
Normal file
BIN
R4.01_R4.A.10/td_tp/tp2/img/lights.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 48 KiB |
BIN
R4.01_R4.A.10/td_tp/tp2/img/ligue1.png
Normal file
BIN
R4.01_R4.A.10/td_tp/tp2/img/ligue1.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 97 KiB |
BIN
R4.01_R4.A.10/td_tp/tp2/img/todo.png
Normal file
BIN
R4.01_R4.A.10/td_tp/tp2/img/todo.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 25 KiB |
72
R4.01_R4.A.10/td_tp/tp2/src/ex1/app.js
Normal file
72
R4.01_R4.A.10/td_tp/tp2/src/ex1/app.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// variables de vue
|
||||||
|
|
||||||
|
const button = document.querySelector("input[type=submit]")
|
||||||
|
const select = document.querySelector("select")
|
||||||
|
const board = document.querySelector(".board")
|
||||||
|
const message = document.querySelector("#message")
|
||||||
|
let lights = null
|
||||||
|
|
||||||
|
// state <-> tableau de booléen pour
|
||||||
|
// stocker l'état des lumières
|
||||||
|
// neighbors <-> tableau des voisins de chaque lumière
|
||||||
|
|
||||||
|
let state = []
|
||||||
|
let neighbors = []
|
||||||
|
|
||||||
|
newBoard = ( (size ) => {
|
||||||
|
let frag = document.createDocumentFragment()
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// la fonction crée dans le dom la grille des lumière
|
||||||
|
|
||||||
|
board.replaceChildren(frag)
|
||||||
|
})
|
||||||
|
|
||||||
|
calcNeighbours = ( (size) =>{
|
||||||
|
|
||||||
|
let neighbors = [];
|
||||||
|
for(let i = 0; i < size; i ++){
|
||||||
|
for(let j = 0; j < size; j++){
|
||||||
|
let v = [];
|
||||||
|
v.push(i*size + j);
|
||||||
|
if ( ( j - 1) >= 0) v.push(i*size + j - 1);
|
||||||
|
if ( ( j + 1) < size) v.push(i*size + j + 1);
|
||||||
|
if ( (i - 1 ) >= 0) v.push(size*(i-1) + j );
|
||||||
|
if ( (i + 1 ) < size) v.push(size*(i+1) + j);
|
||||||
|
neighbors.push(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neighbors
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function play(){
|
||||||
|
let size = select.value
|
||||||
|
newBoard(size)
|
||||||
|
neighbors = calcNeighbours(size)
|
||||||
|
lights = document.querySelectorAll(".board span")
|
||||||
|
state = Array.from({length: size*size}, () => Math.random() < 0.5 ? true:false);
|
||||||
|
state.forEach((el,i)=>{
|
||||||
|
if (el) lights[i].classList.toggle("off")
|
||||||
|
})
|
||||||
|
|
||||||
|
message.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// abonnements
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector(".board")
|
||||||
|
.addEventListener("click", ev => {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// permet de choisir d'éteindre/allumer une lumière et
|
||||||
|
// ses voisines
|
||||||
|
// Il faut en outre detecter la fin de partie
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
button.addEventListener("click",play)
|
||||||
|
|
||||||
|
play()
|
||||||
|
|
49
R4.01_R4.A.10/td_tp/tp2/src/ex1/index.html
Normal file
49
R4.01_R4.A.10/td_tp/tp2/src/ex1/index.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
<!-- Centered viewport -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
|
||||||
|
>
|
||||||
|
<link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h4>Lights</h4>
|
||||||
|
<p>Le but est d'éteindre l'ensemble des lumières.</p>
|
||||||
|
|
||||||
|
<p id="message"></p>
|
||||||
|
<article>
|
||||||
|
<div class="board">
|
||||||
|
<!--div class="row">
|
||||||
|
<span data-num="0"></span>
|
||||||
|
<span data-num="1"></span>
|
||||||
|
<span data-num="2"></span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span data-num="3"></span>
|
||||||
|
<span data-num="4"></span>
|
||||||
|
<span data-num="5"></span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span data-num="6"></span>
|
||||||
|
<span data-num="7"></span>
|
||||||
|
<span data-num="8"></span-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<div role="group">
|
||||||
|
<select aria-label="Select size" required>
|
||||||
|
<option selected value="3">3x3</option>
|
||||||
|
<option value="5">5x5</option>
|
||||||
|
<option value="7">7x7</option>
|
||||||
|
</select>
|
||||||
|
<input type="submit" value="New Game">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
50
R4.01_R4.A.10/td_tp/tp2/src/ex1/style.css
Normal file
50
R4.01_R4.A.10/td_tp/tp2/src/ex1/style.css
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*.grid {
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns : 100px 100px 100px;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
grid-gap: 10px;
|
||||||
|
}*/
|
||||||
|
.board > div {
|
||||||
|
display : flex;
|
||||||
|
}
|
||||||
|
.board span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align:baseline;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
/*padding : 1em;*/
|
||||||
|
margin : 0.1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: radial-gradient(rgb(150,250,150), #00ee00);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board span.off {
|
||||||
|
background: #009900;
|
||||||
|
}
|
||||||
|
.board span.off:hover {
|
||||||
|
background: #3AB903;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board span:hover {
|
||||||
|
background: #3AB903;
|
||||||
|
background: radial-gradient(rgb(66, 255, 66), #065006);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
.wrapper {
|
||||||
|
display : flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
.wrapper .grid {
|
||||||
|
flex-basis : auto;
|
||||||
|
}
|
||||||
|
*/
|
23
R4.01_R4.A.10/td_tp/tp2/src/ex2/data.js
Normal file
23
R4.01_R4.A.10/td_tp/tp2/src/ex2/data.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
let data = [
|
||||||
|
{"idStanding":"2282066","intRank":"1","idTeam":"133714","strTeam":"Paris SG","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/rwqrrq1473504808.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WLWWW","strDescription":"Promotion - Champions League (Group Stage)","intPlayed":"18","intWin":"15","intLoss":"1","intDraw":"2","intGoalsFor":"48","intGoalsAgainst":"13","intGoalDifference":"35","intPoints":"47","dateUpdated":"2023-01-12 23:01:12"},
|
||||||
|
{"idStanding":"2282067","intRank":"2","idTeam":"133822","strTeam":"Lens","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/3pxoum1598797195.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWDWW","strDescription":"Promotion - Champions League (Group Stage)","intPlayed":"18","intWin":"12","intLoss":"1","intDraw":"5","intGoalsFor":"31","intGoalsAgainst":"13","intGoalDifference":"18","intPoints":"41","dateUpdated":"2023-01-12 23:01:12"},
|
||||||
|
{"idStanding":"2282068","intRank":"3","idTeam":"133707","strTeam":"Marseille","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/uutsyt1473504764.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWWWW","strDescription":"Promotion - Champions League (Qualification)","intPlayed":"18","intWin":"12","intLoss":"3","intDraw":"3","intGoalsFor":"36","intGoalsAgainst":"15","intGoalDifference":"21","intPoints":"39","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282069","intRank":"4","idTeam":"133719","strTeam":"Rennes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/ypturx1473504818.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LWLWD","strDescription":"Promotion - Europa League (Group Stage)","intPlayed":"18","intWin":"10","intLoss":"4","intDraw":"4","intGoalsFor":"35","intGoalsAgainst":"20","intGoalDifference":"15","intPoints":"34","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282070","intRank":"5","idTeam":"133823","strTeam":"Monaco","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/819x3z1655593495.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWWLW","strDescription":"Promotion - Europa Conference League (Qualification)","intPlayed":"18","intWin":"10","intLoss":"4","intDraw":"4","intGoalsFor":"35","intGoalsAgainst":"25","intGoalDifference":"10","intPoints":"34","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282071","intRank":"6","idTeam":"133715","strTeam":"Lorient","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/sxsttw1473504748.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWLDL","strDescription":"","intPlayed":"18","intWin":"9","intLoss":"4","intDraw":"5","intGoalsFor":"30","intGoalsAgainst":"26","intGoalDifference":"4","intPoints":"32","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282072","intRank":"7","idTeam":"133711","strTeam":"Lille","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/2giize1534005340.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DDWWD","strDescription":"","intPlayed":"18","intWin":"9","intLoss":"5","intDraw":"4","intGoalsFor":"30","intGoalsAgainst":"24","intGoalDifference":"6","intPoints":"31","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282073","intRank":"8","idTeam":"133713","strTeam":"Lyon","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/blk9771656932845.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLWDL","strDescription":"","intPlayed":"18","intWin":"7","intLoss":"7","intDraw":"4","intGoalsFor":"27","intGoalsAgainst":"21","intGoalDifference":"6","intPoints":"25","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282074","intRank":"9","idTeam":"134713","strTeam":"Clermont Foot","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/wrytst1426871249.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWLLD","strDescription":"","intPlayed":"18","intWin":"7","intLoss":"7","intDraw":"4","intGoalsFor":"22","intGoalsAgainst":"26","intGoalDifference":"-4","intPoints":"25","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282075","intRank":"10","idTeam":"133712","strTeam":"Nice","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/msy7ly1621593859.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WLDDW","strDescription":"","intPlayed":"18","intWin":"6","intLoss":"6","intDraw":"6","intGoalsFor":"22","intGoalsAgainst":"20","intGoalDifference":"2","intPoints":"24","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282076","intRank":"11","idTeam":"133934","strTeam":"Stade de Reims","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/xcrw1b1592925946.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WDWDW","strDescription":"","intPlayed":"18","intWin":"5","intLoss":"4","intDraw":"9","intGoalsFor":"21","intGoalsAgainst":"23","intGoalDifference":"-2","intPoints":"24","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282077","intRank":"12","idTeam":"133703","strTeam":"Toulouse","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/3kqxs61547893229.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWLLL","strDescription":"","intPlayed":"18","intWin":"6","intLoss":"8","intDraw":"4","intGoalsFor":"28","intGoalsAgainst":"33","intGoalDifference":"-5","intPoints":"22","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282078","intRank":"13","idTeam":"134789","strTeam":"Troyes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/swuwpq1426544753.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LWDLD","strDescription":"","intPlayed":"18","intWin":"4","intLoss":"8","intDraw":"6","intGoalsFor":"29","intGoalsAgainst":"35","intGoalDifference":"-6","intPoints":"18","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282079","intRank":"14","idTeam":"133861","strTeam":"Nantes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/8r0dab1598797469.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWDDL","strDescription":"","intPlayed":"18","intWin":"3","intLoss":"6","intDraw":"9","intGoalsFor":"18","intGoalsAgainst":"24","intGoalDifference":"-6","intPoints":"18","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282080","intRank":"15","idTeam":"133709","strTeam":"Montpellier","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/spxtvu1473504783.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLWDD","strDescription":"","intPlayed":"18","intWin":"5","intLoss":"11","intDraw":"2","intGoalsFor":"28","intGoalsAgainst":"37","intGoalDifference":"-9","intPoints":"17","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282081","intRank":"16","idTeam":"133702","strTeam":"Ajaccio","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/qpxvwy1473505505.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLWDW","strDescription":"","intPlayed":"18","intWin":"4","intLoss":"11","intDraw":"3","intGoalsFor":"15","intGoalsAgainst":"27","intGoalDifference":"-12","intPoints":"15","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282082","intRank":"17","idTeam":"133704","strTeam":"Brest","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/z69be41598797026.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLLWL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"3","intLoss":"10","intDraw":"5","intGoalsFor":"18","intGoalsAgainst":"33","intGoalDifference":"-15","intPoints":"14","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282083","intRank":"18","idTeam":"134788","strTeam":"Auxerre","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/lzdtbf1658753355.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLLLD","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"3","intLoss":"11","intDraw":"4","intGoalsFor":"16","intGoalsAgainst":"40","intGoalDifference":"-24","intPoints":"13","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282084","intRank":"19","idTeam":"133882","strTeam":"Strasbourg","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/yuxtyy1464540071.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLLDL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"1","intLoss":"8","intDraw":"9","intGoalsFor":"22","intGoalsAgainst":"33","intGoalDifference":"-11","intPoints":"12","dateUpdated":"2023-01-12 23:01:13"},
|
||||||
|
{"idStanding":"2282085","intRank":"20","idTeam":"134709","strTeam":"Angers","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/445gc21622560255.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLLLL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"2","intLoss":"14","intDraw":"2","intGoalsFor":"16","intGoalsAgainst":"39","intGoalDifference":"-23","intPoints":"8","dateUpdated":"2023-01-12 23:01:13"}
|
||||||
|
]
|
||||||
|
|
67
R4.01_R4.A.10/td_tp/tp2/src/ex2/index.html
Normal file
67
R4.01_R4.A.10/td_tp/tp2/src/ex2/index.html
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="initial-scale=1,witdh=device-width">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
|
||||||
|
<!--link rel="stylesheet" href="./style.css"-->
|
||||||
|
|
||||||
|
<title>Classement Ligue 1</title>
|
||||||
|
</head>
|
||||||
|
<body class="m-6">
|
||||||
|
<main class="container">
|
||||||
|
<h3 class="title is-3 has-text-primary-dark">
|
||||||
|
Classement Ligue 1 de football<span class="tag" id="date"></span>
|
||||||
|
|
||||||
|
<div class="block control has-icons-left is-inline-block is-pulled-right">
|
||||||
|
<input class="input" type="search" placeholder="Equipe" id="myInput">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><abbr title="Position">Pos</abbr><a id="sort" href="#"><span class="icon"><i class="fas fa-sort"></i></span></a></th>
|
||||||
|
<th></th>
|
||||||
|
<th>Team</th>
|
||||||
|
<th><abbr title="Played">Pld</abbr></th>
|
||||||
|
<th><abbr title="Won">W</abbr></th>
|
||||||
|
<th><abbr title="Drawn">D</abbr></th>
|
||||||
|
<th><abbr title="Lost">L</abbr></th>
|
||||||
|
<th><abbr title="Goals for">GF</abbr></th>
|
||||||
|
<th><abbr title="Goals against">GA</abbr></th>
|
||||||
|
<th><abbr title="Goal difference">GD</abbr></th>
|
||||||
|
<th><abbr title="Points">Pts</abbr></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th><abbr title="Position">Pos</abbr></th>
|
||||||
|
<th></th>
|
||||||
|
<th>Team</th>
|
||||||
|
<th><abbr title="Played">Pld</abbr></th>
|
||||||
|
<th><abbr title="Won">W</abbr></th>
|
||||||
|
<th><abbr title="Drawn">D</abbr></th>
|
||||||
|
<th><abbr title="Lost">L</abbr></th>
|
||||||
|
<th><abbr title="Goals for">GF</abbr></th>
|
||||||
|
<th><abbr title="Goals against">GA</abbr></th>
|
||||||
|
<th><abbr title="Goal difference">GD</abbr></th>
|
||||||
|
<th><abbr title="Points">Pts</abbr></th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
<script src="data.js"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
74
R4.01_R4.A.10/td_tp/tp2/src/ex2/script.js
Normal file
74
R4.01_R4.A.10/td_tp/tp2/src/ex2/script.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
renderTable=((mountPoint,standings,keys)=>{
|
||||||
|
let frag = document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (team of standings){
|
||||||
|
let tr = document.createElement("tr");
|
||||||
|
for (key of keys){
|
||||||
|
|
||||||
|
let td = document.createElement("td");
|
||||||
|
let elt;
|
||||||
|
if (key === "strTeamBadge"){
|
||||||
|
elt = document.createElement("img");
|
||||||
|
elt.src= team[key];
|
||||||
|
|
||||||
|
}else{
|
||||||
|
elt = team[key];
|
||||||
|
}
|
||||||
|
td.append(elt)
|
||||||
|
tr.append(td);
|
||||||
|
}
|
||||||
|
frag.append(tr);
|
||||||
|
}
|
||||||
|
mountPoint.replaceChildren();
|
||||||
|
mountPoint.append(frag);
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
sortAndFilter=((standings,sort,filter)=>{
|
||||||
|
|
||||||
|
return standings
|
||||||
|
.filter((team)=>team.strTeam.toLowerCase().includes(filter.toLowerCase()))
|
||||||
|
.sort((teamA,teamB)=>(teamA.intRank - teamB.intRank)*sort)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
debounce=((f,time)=>{
|
||||||
|
let timer
|
||||||
|
return function(...args){
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(()=>f(...args),time)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
window.onload = (()=>{
|
||||||
|
let sort = 1;
|
||||||
|
let text = "";
|
||||||
|
let tbody = document.querySelector("tbody");
|
||||||
|
const keys = ["intRank","strTeam","strTeamBadge","intPlayed","intWin","intLoss","intDraw","intGoalsFor"
|
||||||
|
,"intGoalsAgainst","intGoalDifference","intPoints"];
|
||||||
|
|
||||||
|
renderTable(tbody,data,keys);
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector("#sort")
|
||||||
|
.addEventListener("click",(e)=>{
|
||||||
|
sort = - sort;
|
||||||
|
renderTable(tbody,sortAndFilter(data,sort,text),keys);
|
||||||
|
})
|
||||||
|
document
|
||||||
|
.querySelector("input")
|
||||||
|
.addEventListener("input",debounce((e)=>{
|
||||||
|
text = e.target.value
|
||||||
|
console.log("MAJ")
|
||||||
|
renderTable(tbody,sortAndFilter(data,sort,text),keys);
|
||||||
|
|
||||||
|
},1000))
|
||||||
|
|
||||||
|
})
|
32
R4.01_R4.A.10/td_tp/tp2/src/ex3/css/style.css
Normal file
32
R4.01_R4.A.10/td_tp/tp2/src/ex3/css/style.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
:focus-visible {outline:none;}
|
||||||
|
|
||||||
|
.strike {
|
||||||
|
text-decoration-line : line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader.is-loading {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
overflow: show;
|
||||||
|
margin: auto;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader.is-loading:after {
|
||||||
|
animation: spinAround 500ms infinite linear;
|
||||||
|
border: 2px solid hsl(0deg, 0%, 86%);
|
||||||
|
border-radius: 9999px;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
top: calc(50% - 5em);
|
||||||
|
left: calc(50% - 5em);
|
||||||
|
width: 10em;
|
||||||
|
height: 10em;
|
||||||
|
border-width: 0.25em;
|
||||||
|
}
|
48
R4.01_R4.A.10/td_tp/tp2/src/ex3/index.html
Normal file
48
R4.01_R4.A.10/td_tp/tp2/src/ex3/index.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>Todo</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="./css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container content is-max-desktop p-4">
|
||||||
|
|
||||||
|
<div id="loader"></div>
|
||||||
|
<div class="has-text-centered">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- formulaire pour la saisie -->
|
||||||
|
|
||||||
|
<form id="add_form" class="block">
|
||||||
|
<p class="control has-icons-left is-expanded">
|
||||||
|
<input autocomplete="off" id="input_todo" class="input is-large is-expanded" type="text" placeholder="Enter Todo">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- filtrage de l'affichage -->
|
||||||
|
|
||||||
|
<div id="todos_filter" class="tabs is-small is-centered">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active"><a href="#/" id="all">All</a></li>
|
||||||
|
<li><a href="#/active" id="active">Active</a></li>
|
||||||
|
<li><a href="#/done" id="done">Done</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Affichage des todos -->
|
||||||
|
<section id="todos_list">
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script type="module" src="./js/app.js"></script>
|
||||||
|
|
||||||
|
</html>
|
6
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/app.js
Normal file
6
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/app.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Controller from './controller.js'
|
||||||
|
import Model from './model.js'
|
||||||
|
import View from './view.js'
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Controller(new Model(),new View())
|
54
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/controller.js
Normal file
54
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/controller.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
export default class Controller {
|
||||||
|
constructor(model, view) {
|
||||||
|
this.model = model
|
||||||
|
this.view = view
|
||||||
|
this.filter = "all"
|
||||||
|
this.view.bindAddTodo(this.addTodo.bind(this))
|
||||||
|
this.view.bindDeleteTodo(this.deleteTodo.bind(this))
|
||||||
|
this.view.bindToggleTodo(this.toggleTodo.bind(this))
|
||||||
|
this.view.bindEditTodo(this.editTodo.bind(this))
|
||||||
|
|
||||||
|
/** Routage **/
|
||||||
|
this.routes = ['all','active','done'];
|
||||||
|
|
||||||
|
/** Routage **/
|
||||||
|
window.addEventListener("load",this.routeChanged.bind(this));
|
||||||
|
window.addEventListener("hashchange",this.routeChanged.bind(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
routeChanged(){
|
||||||
|
let route = window.location.hash.replace(/^#\//,'');
|
||||||
|
this.filter = this.routes.find( r => r === route) || 'all';
|
||||||
|
this.filterTodoList();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
filterTodoList () {
|
||||||
|
let todos = this.model.getTodos(this.filter)
|
||||||
|
this.view.renderTodoList(todos)
|
||||||
|
this.view.setFilterTabs(this.filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTodo(text) {
|
||||||
|
let todo = this.model.add(text)
|
||||||
|
this.filterTodoList()
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTodo(id) {
|
||||||
|
this.model.delete(parseInt(id))
|
||||||
|
this.filterTodoList()
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTodo(id) {
|
||||||
|
this.model.toggle(parseInt(id))
|
||||||
|
this.filterTodoList()
|
||||||
|
}
|
||||||
|
|
||||||
|
editTodo(id, text) {
|
||||||
|
this.model.edit(parseInt(id),text)
|
||||||
|
this.filterTodoList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
77
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/model.js
Normal file
77
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/model.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const todos = [
|
||||||
|
{
|
||||||
|
id : 1,
|
||||||
|
text : "apprendre le javascript",
|
||||||
|
done : false
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id : 2,
|
||||||
|
text : "Faire des maths",
|
||||||
|
done : false
|
||||||
|
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default class Model {
|
||||||
|
constructor() {
|
||||||
|
//this.todos = JSON.parse(localStorage.getItem('todos')) || []
|
||||||
|
this.todos = JSON.parse(localStorage.getItem('todos')) || todos
|
||||||
|
}
|
||||||
|
|
||||||
|
_commit(todos) {
|
||||||
|
localStorage.setItem('todos', JSON.stringify(todos))
|
||||||
|
}
|
||||||
|
|
||||||
|
getTodos(filter){
|
||||||
|
if (filter == "active")
|
||||||
|
return this.todos.filter(todo => !todo.done)
|
||||||
|
if (filter == "done")
|
||||||
|
return this.todos.filter(todo => todo.done)
|
||||||
|
|
||||||
|
return this.todos
|
||||||
|
}
|
||||||
|
|
||||||
|
add(todoText) {
|
||||||
|
const todo = {
|
||||||
|
id: this.todos.length > 0 ? this.todos[this.todos.length - 1].id + 1 : 1,
|
||||||
|
text: todoText,
|
||||||
|
done : false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.todos.push(todo)
|
||||||
|
this._commit(this.todos)
|
||||||
|
return todo
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(id, updatedText) {
|
||||||
|
let todo = this.todos.find(t => t.id === id)
|
||||||
|
todo.text = updatedText
|
||||||
|
// this.todos = this.todos.map(todo =>
|
||||||
|
// todo.id === id ? { id: todo.id, text: updatedText, done: todo.done} : todo
|
||||||
|
// )
|
||||||
|
|
||||||
|
this._commit(this.todos)
|
||||||
|
}
|
||||||
|
:q!
|
||||||
|
|
||||||
|
delete(id) {
|
||||||
|
this.todos = this.todos.filter(todo => todo.id !== id)
|
||||||
|
|
||||||
|
this._commit(this.todos)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(id) {
|
||||||
|
let todo = this.todos.find(t => t.id === id)
|
||||||
|
todo.done = !todo.done
|
||||||
|
//this.todos = this.todos.map(todo =>
|
||||||
|
// todo.id === id ? { id: todo.id, text: todo.text, done: !todo.done } : todo
|
||||||
|
//)
|
||||||
|
this._commit(this.todos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
95
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/view.js
Normal file
95
R4.01_R4.A.10/td_tp/tp2/src/ex3/js/view.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
export default class View {
|
||||||
|
constructor() {
|
||||||
|
this.form = document.querySelector("#add_form")
|
||||||
|
this.input = document.querySelector("#input_todo")
|
||||||
|
this.list = document.querySelector("#todos_list")
|
||||||
|
this.tabs = document.querySelector("#todos_filter")
|
||||||
|
this.loader = document.querySelector("#loader")
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTodo() {
|
||||||
|
return this.input.value
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetInput() {
|
||||||
|
this.input.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
_getNewTodoElement(todo){
|
||||||
|
let div = document.createElement("div")
|
||||||
|
div.classList.add("box","is-flex","is-align-items-center","m-2")
|
||||||
|
div.dataset.id = todo.id
|
||||||
|
|
||||||
|
let input = document.createElement("input")
|
||||||
|
input.type = "checkbox"
|
||||||
|
input.classList.add("mr-3")
|
||||||
|
|
||||||
|
let span = document.createElement("p")
|
||||||
|
span.classList.add("mb-0","is-size-3","mr-auto")
|
||||||
|
span.textContent = todo.text
|
||||||
|
span.setAttribute("contenteditable",true)
|
||||||
|
|
||||||
|
|
||||||
|
let button = document.createElement("button")
|
||||||
|
button.classList.add("delete","is-large")
|
||||||
|
|
||||||
|
|
||||||
|
if (todo.done){
|
||||||
|
span.classList.add("strike")
|
||||||
|
input.checked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
div.append(input,span,button)
|
||||||
|
|
||||||
|
return div
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoader(){
|
||||||
|
this.loader.classList.add("is-loading")
|
||||||
|
}
|
||||||
|
unsetLoader(){
|
||||||
|
this.loader.classList.remove("is-loading")
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilterTabs(filter){
|
||||||
|
let li = this.tabs.querySelectorAll("li")
|
||||||
|
li.forEach( tab => {
|
||||||
|
tab.classList.remove("is-active")
|
||||||
|
})
|
||||||
|
let active = this.tabs.querySelector(`#${filter}`)
|
||||||
|
active.parentNode.classList.add("is-active")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTodoList(todos) {
|
||||||
|
let list = new DocumentFragment()
|
||||||
|
for (let todo of todos){
|
||||||
|
list.appendChild(this._getNewTodoElement(todo))
|
||||||
|
}
|
||||||
|
this.list.replaceChildren(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Abonnements événements **/
|
||||||
|
|
||||||
|
bindAddTodo(handler) {
|
||||||
|
this.form.addEventListener("submit", (e=>{
|
||||||
|
e.preventDefault()
|
||||||
|
let text = this._getTodo()
|
||||||
|
handler(text)
|
||||||
|
this._resetInput()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bindDeleteTodo(handler) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEditTodo(handler) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
bindToggleTodo(handler) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user