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
|
||||
[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