This commit is contained in:
Denis Monnerat 2025-02-07 11:41:41 +01:00
parent b89351ab51
commit 27e24c917e
38 changed files with 752 additions and 1 deletions

@ -4,6 +4,10 @@
[Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
#### Semaine 2
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2)
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2_bis](./td_tp/tp2_bis)
#### Semaine 3
[Promesses, Ajax, API de données](cours/ajax.pdf), [tp3](./td_tp/tp3)

Binary file not shown.

@ -0,0 +1,44 @@
# TP javascript : DOM
Un peu [d'aide](./aide.md).
#### Ex1
Complétez le code du fichier <code>[game.js](./src/ex1/js/game.js)</code> inclus
dans <code>[cookie.html](./src/ex1/cookie.html)</code> afin de concevoir un idle game
consistant à cliquer sur un cookie le plus grand nombre de fois en 15
secondes.
<div style="text-align:center">
<img src="./img/cookie1.png">
<img src="./img/cookie2.png">
</div>
#### Ex2 : memory
Le jeu tirera aléatoirement des ampoules allumées, les dévoilera
pendant une seconde au joueur, puis les cachera à nouveau.
Le joueur doit alors cliquer sur chaque ampoule initialement allumée. S'il se trompe,
une alerte lui signifie qu'il a perdu. Lorsque toutes les ampoules ont été retrouvées, le joueur gagne.
![memory](./img/memory.png?style=centerme)
Vous devez seulement complèter le fichier `memory.js`
#### Ex3 **Glisser/Déposer** (pour ceux qui ont **tout** fait)
Le but est de trier un tableau d'entiers, par ordre croissant, par glisser/déposer (drag and drop).
<img src="./img/tri.png" width="100%" class="img-polaroid">
<img src="./img/tri1.png" width="100%" class="img-polaroid">
<img src="./img/tri2.png" width="100%" class="img-polaroid">
Travail à faire
- La génération des entiers dans les divisions "draggable".
- La sensibilisation aux événements <code>dragstart, drop, dragover</code>. Vous trouverez toute l'information utilie sur l'interface drag and drog d'html5 <a target="_blank" href="http://www.w3schools.com/html/html5_draganddrop.asp">ici</a>.
- Dans la zone correspondante au tableau trié, le fond change de couleur suivant que les entiers déjà déposés sont triés correctement ou non.
Pour gérer les classes css d'un noeud, vous pouvez utilisez la propriété <a href="https://developer.mozilla.org/fr/docs/Web/API/Element/classList" target="_blank">classList</a>.

@ -0,0 +1,118 @@
#### 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.
```
#### 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")
```

Binary file not shown.

After

(image error) Size: 256 KiB

Binary file not shown.

After

(image error) Size: 271 KiB

Binary file not shown.

After

(image error) Size: 62 KiB

Binary file not shown.

After

(image error) Size: 44 KiB

Binary file not shown.

After

(image error) Size: 45 KiB

Binary file not shown.

After

(image error) Size: 46 KiB

@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Cookie clicker</title>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<div class="container">
<h1>Cookie clicker</h1>
<figure>
<img src="img/cookie.png" alt="cookie">
<span>0</span>
</figure>
<button>start</button>
<p class="hidden">
<img src="img/memee.png" alt="memee" width="64" height="64">
<span></span>
</p>
</div>
<script type="text/javascript" src="./js/game.js"></script>
</body>
</html>

Binary file not shown.

After

(image error) Size: 54 KiB

@ -0,0 +1,63 @@
@import url(https://fonts.googleapis.com/css?family=Kavoon);
body {
background-image: url("img/bg.jpg");
background-repeat: repeat;
font-family: Kavoon;
font-size: 24px;
text-align: center;
color: white;
}
.container {
max-width: 320px;
margin: auto;
}
h1 {
font-size: 28px;
background-color: rgba(0, 0, 0, 0.25);
padding: 10px 40px 10px 40px;
border-radius: 10px;
}
figure {
position: relative;
margin: 0;
}
figure > span {
position: absolute;
top: 25px;
right: 50px;
border-radius: 21px;
height: 42px;
width: 56px;
background-color: #f4d03f;
box-shadow: 4px 4px 4px black;
padding-top: 9px;
}
button {
display: block;
width: 100%;
font-family: Kavoon;
font-size: 24px;
background-color: white;
padding: 9px;
border-radius: 10px;
border-width: 0;
}
p {
text-align: left;
}
img[alt="GrandMa"] {
float: left;
}
.hidden {
display: none;
}

Binary file not shown.

After

(image error) Size: 54 KiB

Binary file not shown.

After

(image error) Size: 103 KiB

Binary file not shown.

After

(image error) Size: 11 KiB

@ -0,0 +1 @@
// TODO

@ -0,0 +1,11 @@
img{
cursor:pointer;
}
#gagne,#perdu,.hidden{
display : none;
}
.progress::-webkit-progress-value {
transition: width 0.5s ease;
}

Binary file not shown.

After

(image error) Size: 9.1 KiB

Binary file not shown.

After

(image error) Size: 9.5 KiB

@ -0,0 +1 @@
// TODO

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1,width=device-width">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<link rel="stylesheet" href="./css/style.css">
<script src="./js/memory.js"></script>
<title>Memory</title>
</head>
<body class="m-4">
<main class="container">
<h1 class="title is-1 has-text-primary">Memory</h1>
<div class="columns">
<div class="column is-6">
<div class="columns">
<div class="column">
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
</div>
<div class="column">
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
</div>
<div class="column">
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
<img src="./images/off.png" alt="" />
</div>
</div>
</div>
<div class="column is-6">
<h3 class="title is-3 has-text-primary">Règles</h3>
<p class="block is-size-4">
Pendant une seconde, vous verrez l'état de l'ensemble
des ampoules, puis elles seront éteintes. Vous devrez
alors cliquer sur celles qui étaient allumées.
</p>
<button class="block button is-info">COMMENCER</button>
<progress id="temps" class="progress is-primary" value="100" max="100"></progress>
<!--div id="temps">10</div-->
<div class="notification is-danger is-size-4 p-4" id="perdu">
<p>Perdu ! </p>
</div>
<div class="notification is-success is-size-4 p-4" id="gagne">
<p>Gagné ! </p>
</div>
</div>
</div>
</main>
</body>
</html>

@ -0,0 +1,46 @@
.slot{
width:52px;
height:52px;
padding:2px;
margin:11px;
border:1px solid #aaaaaa;
background-color:#ffffff;
display:inline-block;
vertical-align:middle;
}
.drag{
width:50px;
height:50px;
border:1px solid #aaaaaa;
background-color:#eeeeee;
text-align:center;
font-size:25px;
line-height:52px;
}
.ct{
height:100px;
margin:10px;
line-height:100px;
text-align:center;
}
.normal{
background-color:#1583CC;
}
.good{
background-color:#00867F;
}
.wrong{
background-color:#eeaaaa;
}
[draggable=true] {
cursor: move;
}

@ -0,0 +1,3 @@
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title></title>
<script src="./js/tri.js"></script>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<h3>Drag and Drop</h3>
<!-- Le tableau initial -->
<div class="ct normal" id="start">
<div class="slot">
<div id="drag1" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag2" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag3" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag4" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag5" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag6" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag7" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag8" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag9" class="drag" draggable="true"></div>
</div>
<div class="slot">
<div id="drag10" class="drag" draggable="true"></div>
</div>
</div>
<!-- Le tableau doit être trié ici -->
<div class="ct good" id="end">
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
<div class="slot"></div>
</div>
</body>
</html>

@ -0,0 +1,36 @@
# TP javascript : Ajax, promesses.
Vous disposez tous, à la racine de votre compte, d'un répertoire `public_html` qui est servi (http/https) par le serveur dwarves.
Pour accéder à vos pages, utilisez l'url :
> http(s)://dwarves.[arda|iut-fbleau.fr]/~login
Pour tester les différentes api http, vous pouvez utilisez la commande `curl`.
#### Ex1
Le but est de faire une recherche de film en utilisant l'api [OMDb](https://www.omdbapi.com/) (Open Movie Database).
![movie](img/search.png?style=centerme)
Son utilisation nécessite une clé. Vous pouvez utiliser la mienne `2fcb2848`. Si
la limite de requêtes est atteinte, générez votre propre clé.
Complétez le [code](./src/ex1).
- J'ai volontairement utilisé des modules. Il vous faut donc tester l'application avec
http.
- Écrire la fonction du modèle
```js
getMovies(search)
```
Cette fonction renvoie une promesse qui permet de faire une requête ajax vers l'api OMDb. Pour ajax,
utilisez d'abord l'interface `fetch`, puis l'objet `XMLHttpRequest`.
- Ecrire la fonction du contrôleur
```js
async search(searchMovie)
```
qui permet de faire une recherche. Cette fonction utilise évidemment la fonction précédente `getMovies`.

@ -0,0 +1,35 @@
#### XMLHttpRequest
```js
let xhr = new XMLHttpRequest()
xhr.open("GET","mon/url")
xhr.onload = ...
xhr.onerror = ...
xhr.send()
```
#### Promise
```js
let promise = new Promise ((resolve,reject) => {
setTimeout(() => {
resolve("OK")
},1000)
})
promise.then(data => ....)
```
#### async/await
```js
function getUser(email){
return new Promise(...)
}
async function updateUser(){
let user = await getUser(...)
console.log(user)
}
```

Binary file not shown.

After

(image error) Size: 125 KiB

Binary file not shown.

After

(image error) Size: 984 KiB

Binary file not shown.

After

(image error) Size: 320 KiB

@ -0,0 +1,87 @@
main {
position: absolute;
top: 2rem;
left: 50%;
transform: translate(-50%, 0);
}
.loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader {
margin: 6rem 0 0;
}
.error {
color: #FFFAAA;
margin: 1rem 0;
}
label {
align-items: center;
display: flex;
flex-direction: column;
font-size: 1.6rem;
}
label span {
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
}
input {
margin: 1rem 0 0;
font-size: 1.6rem;
font-weight: 300;
padding: 0.8rem 1rem;
color: white;
border: 1px solid rgba(255, 255, 255, 0.05);
background: rgba(255, 255, 255, 0.05);
transition: all 0.3s;
box-shadow: 1px 1px 2px rgba(0,0,0, 0.3);
-moz-appearance:none;
-webkit-appearance:none;
outline: none;
}
input:focus {
border: 1px solid transparent;
background: rgba(255, 255, 255, 0.08);
}
ul {
padding: 0;
margin: 1rem 0 2rem;
}
ul li {
padding: 0.6rem 1rem;
margin: 1px 0;
line-height: 1.4rem;
display: flex;
justify-content: space-between;
align-items: center;
align-content: space-between;
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
background: rgba(255, 255, 255, 0.08);
box-shadow: 0 0 2px rgba(0,0,0, 0.3);
box-sizing: border-box;
}
ul li:hover,
ul li:active,
ul li:focus {
background: rgba(255, 255, 255, 0.1);
}
ul li a {
margin: 0 0.6rem 0 0;
text-decoration: none;
color: white;
}
ul li span {
opacity: 0.5;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial;
font-weight: 300;
background-size: cover;
background-attachment: fixed;
background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000);
background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000);
color: white;
}

@ -0,0 +1,13 @@
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
<g fill="none" fill-rule="evenodd" stroke-width="2">
<circle cx="22" cy="22" r="1">
<animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
<animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
</circle>
<circle cx="22" cy="22" r="1">
<animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
<animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
</circle>
</g>
</svg>

After

(image error) Size: 1.0 KiB

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1,width=device-width">
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
<body>
<!-- vue html -->
<main>
<label>
<span>Search a movie</span>
<input type="input" placeholder="28 Days Later..">
</label>
<div class="loader">
<img src="./img/puff.svg">
</div>
<p class="error"></p>
<div id="list-movies">
</div>
</main>
</body>
<script type="module" src="./js/app.js"></script>
</html>

@ -0,0 +1,7 @@
import model from './model.js'
import Controller from './controller.js'
import View from './view.js'
const view = new View()
const app = new Controller(view,model)

@ -0,0 +1,26 @@
class Controller {
constructor(view,model){
this.view = view
this.model = model
this.loading = false
this.lastSearch = null
this.error = null
this.results = []
this.view.setLoading(false)
this.view.bindSearch(this.search.bind(this))
}
reset() {
this.loading = false
this.error = null
this.results = []
}
async search(searchMovie) {
// TODO
}
}
export default Controller

@ -0,0 +1,12 @@
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
function debounce(fn, wait) {
let timeout
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(() => fn(...args), wait)
}
}
export default debounce

@ -0,0 +1,9 @@
let apiKey = '2fcb2848'
let model = {
getMovies(search){
// TODO
}
}
export default model

@ -0,0 +1,54 @@
import debounce from "./helpers.js"
class View {
constructor(){
this.listMovies = document.querySelector("#list-movies")
this.inputSearch = document.querySelector("input")
this.loader = document.querySelector(".loader")
this.message = document.querySelector("p.error")
}
_getInput(){
return this.inputSearch.value
}
setLoading(loading){
if (loading)
this.loader.style.display = "block"
else
this.loader.style.display = "none"
}
renderMessage(error){
this.message.style.display = "block"
this.message.textContent = error
}
renderList(movies){
let ul = document.createElement("ul")
movies.forEach((movie)=>{
let li = document.createElement("li")
let a = document.createElement("a")
let span = document.createElement("span")
a.href = `http://www.imdb.com/title/${movie.imdbID}`
a.target="_blank"
a.textContent = movie.Title
span.textContent = movie.Year
li.appendChild(a)
li.appendChild(span)
ul.appendChild(li)
})
this.listMovies.replaceChildren(ul)
}
bindSearch(handler){
this.inputSearch.addEventListener("input",debounce((e)=>{
handler(this._getInput())
},500))
}
}
export default View