Pour votre progression, c'est très important de finir le sujet précédent avant de commencer celui-ci.
Vous devez travailler connectés (lien « Connexion » en haut à droite de la page). Sinon, vous rencontrerez des problèmes d'avancement et vous risquez d'être comptés absent. En étant connectés vous retrouverez l'endroit où vous en étiez à la séance suivante.
Commençons par une rapide révision des notions des sujets précédents.
<script>
console.log('abc');
setTimeout(()=>{console.log('def');},5000);
console.log('ghi');
</script>
Il s'écoule à peu près 5 secondes entre l'affichage dans la console de abc et ghi
Non. (rappel animation setTimeout)
Il s'écoule à peu près 5 secondes entre l'affichage dans la console de abc et def
la fonction setTimeout met 5 secondes à s’exécuter
Non, setTimeout ne fait qu'indiquer au navigateur qu'il faudra appeler la fonction anonyme dans 5 secondes. setTimeout s'execute très vite (moins de 2ms)
(rappel animation setTimeout)
<script> console.log('abc'); document.body.style.backgroundColor='red';
// "bla bla"
document.addEventListener('click',()=>{
console.log('def');
document.body.style.backgroundColor='green';
while(true);
}); console.log('ghi'); </script>
ghi s'affiche dans la console au chargement de la page
Quand l’exécution arrive à "bla bla", la page est affichée en rouge.
Non. L'affichage n'a pas encore été mis à jour. Il n'est mis à jour qu'une fois que le code JS a rendu la main au navigateur et que celui-ci est retourné dans la boucle d'événements. (rappel animation js affichage)
Quand l'utilisateur clique, la page s'affiche en vert.
Non. Quand l'utilisateur clique, le JS reste coincé dans une boucle infinie. Il ne rend donc pas la main au navigateur. Le navigateur ne retourne pas dans la boucle d’événements et ne peut donc pas rafraîchir l'affichage. (rappel animation js bloquant)
La fonction votez_pour_moi() retourne une promesse.
On suppose qu'on a déjà écrit:
let p=votez_pour_moi();
Que faut-il écrire pour afficher « Incroyable! » dans la console, si la promesse est tenue ?
Merci d'utiliser des accolades pour votre fonction.
Vous êtes sur la bonne voie. Le début est correct:
p.then(()=>{ ...
Le problème est après.
Vous êtes sur la bonne voie. Le début est correct:
p.then(...
Le problème est après.
Comme la promesse à déjà été mise dans la variable « p » vous devez utiliser « p ». Vous ne pouvez pas rappeler la fonction votez_pour_moi
La fonction .then(...) doit s'appliquer sur quelque-chose ...
Vous avez écrit « fulfilled ». On ne manipule directement l'état des promesses. On fourni une fonction à .then qui est appelée quand l'état devient « fulfilled »
Dans combien d'états différents peut se trouver une promesse ?
Quels sont les 3 états d'une promesse ?
en attente
tenue
rompue
contente
:-)
re-initialisée
en cours de validation
suspendue
affamée
amoureuse
<3
La fonction je_t_aimerais_pour_toujours() retourne une promesse.
On suppose qu'on a déjà écrit:
let p=je_t_aimerais_pour_toujours();
Que faut-il écrire pour afficher « Snif snif. » dans la console, si la promesse est rompue ?
(utilisez une fonction fléchée sans paramètre et avec des accolades)
Vous êtes sur la bonne voie. Le début est correct:
p.catch(...
Le problème est après
Vous avez écrit « .then »
.then permet de fournir une fonction qui sera appelée lorsque la promesse sera tenue
Ici, ce n'est pas ce qui nous intéresse. On veut fournir une fonction qui sera appelée quand la promesse sera rompue
Il faut effectivement utiliser « catch ». Vérifiez le reste.
La fonction « fais_tes_devoirs » retourne une promesse.
Quand les devoirs sont faits, on veut appeler une fonction « fini ».
Si les devoirs n'ont pas été faits, on veut appeler une fonction « echec ».
Écrivez le JS sans utiliser de variable intermédiaire.
Attention. Vous confondez l'appel à une fonction avec la fonction elle même.
La fonction fais_tes_devoirs ne prend pas de paramètre.
Vous avez écrit
fais_tes_devoirs.then(...
.then est une méthode (fonction) qui s'applique sur une promesse.
fais_tes_devoirs est une fonction qui retourne, mais vous ne l'avez pas appelé, vous avez juste écrit son nom.
Donc vous ne récupérez pas la promesse qu’elle retourne.
Vous avez fourni une fonction anonyme à .then()
Ce n'est pas nécessaire de passer par une fonction anonyme. Vous avez déjà la fonction « fini »
Indice: votre réponse devrait commencer par « fais_tes_devoirs »
Dans la question précédente, on a juste géré les devoirs:
fais_tes_devoirs().then(fini).catch(echec);
Maintenant, on voudrait aussi que la chambre soit rangée, mais uniquement après que les devoirs soient finis. Ensuite, après les devoirs et le rangement, on voudrait afficher « Ouf, enfin libre ! » dans la console.
On dispose d'une fonction « range_ta_chambre », qui retourne une promesse et qui ne prend pas de paramètre.
On veut utiliser cette ligne pour enchaîner:
fais_tes_devoirs().then(fini).then(()=>{console.log('Ouf, enfin libre!')}).catch(echec);
Voici la fonction fini:
function fini(){
console.log('Les devoirs sont finis.');
XYZ
}
Que faut-il écrire à la place de XYZ ?
Presque. Tel que vous l'avez écrit, la chambre est bien rangée après les devoirs.
Par contre « Ouf, enfin libre » est affiché avant la fin du rangement.
Il faut ajouter quelque-chose pour pouvoir enchaîner les appels.
Rappel sur cet enchaînement.
<script XYZ>
ICI
</script>
Que faut-il écrire à la place de XYZ pour pouvoir utiliser « await » à l'endroit indiqué « ICI » ?
Vous êtes sur la bonne voie. Vérifiez la syntaxe exacte.
Il est effectivement question de « module ». Vérifiez le reste...
<script>
XYZ function exemple() {
ICI
}
</script>
Que faut-il écrire à la place de XYZ pour pouvoir utiliser await à l'endroit indiqué « ICI » ?
<script type="module">
console.log('début');
XYZ
console.log('merci');
</script>
La fonction je_te_rendrais_ton_livre() retourne une promesse.
Que faut-il écrire écrire à la place de « XYZ » pour que « merci » ne soit affiché qu'une fois que le livre rendu ?
Non. « .then() » prend en paramètre une fonction.
« console.log('merci'); » n'est pas dans une fonction que vous pouvez appeler à cet endroit.
Il faut donc utiliser une autre approche ... qu'on a vu au dernier sujet.
Vous avez utilisé la fonction « attendre ».
La fonction « attendre » était juste un exemple qu'on a utilisé dans le cours.
Ici, il n'y a pas de fonction « attendre ».
La réponse commence bien par « await », le problème est après.
Dans cet exemple, l'utilisateur clique sur un lien « http://musique.org/page.html » et une page HTML très simple s'affiche.
1
2
3
4
En pratique les pages HTML font référence à d'autres ressources (CSS, JS, images, ...) qui se trouvent sur le serveur.
Par exemple: page.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Musique</title>
</head>
<body>
<h1>Musicien</h1>
<img src="photo.jpg" alt="photo"/>
<script src="exemple.js"></script>
</body>
</html>
Ouvrez la page suivant dans un autre onglet:
https://moodle.iutv.univ-paris13.fr/img/bjs2/page-html2.html
Ouvrez la console, puis cliquez sur l'onglet « réseau » des outils de dev (au-dessus de la console).
Ensuite appuyez sur le bouton pour recharger la page, tout en appuyant sur la touche Maj (important!).
Vous voyez toutes les requêtes réseau qui sont faites.
A chaque requête un fichier (HTML, image ou CSS) est téléchargé.
Combien de fichiers sont au total téléchargés par le navigateur ?
(y compris la page HTML)
Non. Assurez vous d'appuyer sur Maj quand vous rechargez!
Non. Assurez vous d'appuyer sur Maj quand vous rechargez!
Recommencez avec la page suivante, aussi dans un autre onglet:
https://moodle.iutv.univ-paris13.fr
Regardez bien toutes les requêtes. Essayez de voir quel type de données correspondent à chaque requête.
Combien de requêtes sont faites au total ?
1
Entre 2 et 10
Entre 10 et 100
Entre 100 et 1000
Plus de 1000
Toutes le requêtes qu'on a vu, sont faites au chargement de la page.
Le navigateur reçoit le HTML initial et le traite. Quand il y trouve des ressources externes (images, JS, CSS, ...); il les télécharge.
Dans le premier exemple, quand le navigateur voit ceci:
<img id="chat" src="chat.png" alt="chat"/>
il déclenche le téléchargement de https://moodle.iutv.univ-paris13.fr/img/bjs2/chat.png
Les interactions avec le serveur sont aussi possibles après le chargement de la page:
Voyons quelques exemples.
Quand un utilisateur commence à taper une recherche dans Google, Google lui propose une liste de suggestions.
Il est impossible d'inclure dans la page d'origine toutes les suggestions possibles. Le navigateur va donc demander au serveur la liste de suggestions au fur et à mesure que l'utilisateur écrit. Ceci se fait sans recharger la page.
Dans ce qu'on a vu jusqu'à maintenant, les interactions avec le serveur se faisaient au moment du chargement de la page.
Ici, la liste des suggestions est cherchée à partir du serveur, à partir du JS et après la fin du chargement de la page.
Ouvrez https://www.google.fr
Ouvrez la console.
Vérifiez que l'option « XHR » est bien activée:
Tapez une recherche. Vous devriez voir des suggestions au fur et à mesure que vous tapez.
Vous devriez aussi voir des requêtes Ajax dans la console.
Essayez de trouver le texte que vous avez tapé.
Vous devriez voir des URLs un peu comme ceci:
https://www.google.fr/.../search?XYZ=le-texte-que-vous-avez-tape
Que voyez vous à la place de XYZ ?
En effet. A chaque fois que vous écrivez dans la recherche, il y a une requête qui est faite pour aller chercher des suggestions.
Dans cet exemple, l'utilisateur appuie sur un bouton « J'aime » dans les commentaires d'une vidéo sur Youtube. Son action est enregistrée sur le serveur, mais la page ne se recharge pas.
Le fonctionnement habituel en HTML du bouton consiste à envoyer une requête POST et afficher une nouvelle page. Le rechargement de la page est désagréable pour l'utilisateur. C'est lent, la vidéo s'interrompt, il perd sa position dans la page...
Ici, la requête sur le serveur se fait sans rechargement de la page.
AJAX veut dire « Asynchronous JavaScript and XML »
En pratique, les requêtes Ajax peuvent se faire de différentes manières:
Créez le fichier fetch.html suivant:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Fetch</title>
<script src="https://moodle.iutv.univ-paris13.fr/img/bjs2/bjs2-js-lib.js"></script>
</head>
<body>
<h1>Fetch</h1>
<script type="module">
</script>
</body>
</html>
Tapez dans la console:
simple_fetch('https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php');
En premier est affiché la valeur de retour de simple_fetch(...).
Quel est son type ?
Presque! Regardez la console pour vérifier l'orthographe.
Vous avez mis des choses en trop. On demande juste le type.
En pratique, on a souvent besoin de regarder ce qu'il se passe dans une requête.
Allez sur la page fetch.html où vous avez tapé dans la console:
simple_fetch('https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php');
À l'intérieur de la console, trouvez la requête (XHR GET -- flèche rouge) et ouvrez-là.Vous devez juste regarder dans la console et trouver le nom de l'onglet.
Avez vous bien activé XHR ?
On peut envoyer des informations au serveur, tout simplement en les mettant dans l'URL d'une requête.
On commence les paramètre GET avec un « ? ». Ensuite on met le nom (abc) et sa valeur (def) associée:
http://exemple.org/ma-page.php?abc=def
Si on veut ajouter plusieurs valeurs, on utilise un « & »:
http://exemple.org/ma-page.php?abc=def&rst=uuuuu
Écrivez l'URL suivante en ajoutant un paramètre GET appelé « nom » et ayant comme valeur « Tom »
https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php
Vous êtes sur la bonne voie.
On vous demande l'URL, pas la réponse du serveur.
On vous demande de répondre par une URL qui commence par
https://moodle.iutv.univ-paris13.fr
Dans la console de la page fetch.html, appelez simple_fetch avec l'URL:
https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php?nom=Tom
Quelle est la réponse du serveur ?
Indice: utilisez la fonction simple_fetch dans la console et regardez la réponse dans la requête http.
Dans votre répertoire ~/public_html
créez le fichier cote-serveur.php :
<?php
// Autoriser les requêtes AJAX à partir de n'importe où
header('Access-Control-Allow-Origin: *');
// Dire au navigateur qu'on lui envoie du JSON (et pas du HTML)
header('Content-Type: application/json');
// Message où on concatène le paramètre GET (s'il existe).
$message='Hello'.(isset($_GET['nom']) ? ' '.$_GET['nom'] : '!');
// Convertir le message en JSON (pas très utile ici, mais en général ça l'est).
echo json_encode($message);
?>
Ouvrez le fichier dans votre navigateur avec:
http://localhost/~__user_nb__/cote-serveur.php
Dans la console de la page fetch.html (et pas la page PHP) essayez de faire une requête avec simple_fetch sur
http://localhost/~__user_nb__/cote-serveur.php?nom=Tom
Dans la console, regardez la requête. Que contient la "Réponse" ?
Presque! Vérifiez que vous avez utilisé l'URL au complet.
Vous devez faire une requête dans la console avec simple_fetch(...), puis, dans la console, regarder l'onglet « Réponse » de la requête.
Si quelque-chose ne marche pas, demandez de l'aide à votre enseignant.
Ici, en TP, votre serveur est sur la même machine que le navigateur (client).
Mais, pour ne pas s'emmêler, il vaut mieux s'imaginer que c'est des machines différentes:
Dans les pages suivantes, on va retrouver notre petit chat tout mignon.
Créez les images suivantes (attention elles sont un peu différentes que celles utilisées avant):
Créez le fichier miam.html suivant:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Miam</title>
<script src="https://moodle.iutv.univ-paris13.fr/img/bjs2/bjs2-js-lib.js"></script>
<link rel="stylesheet" href="miam.css"/>
</head>
<body>
<div id="terrain">
<img id="chat" src="chat2.svg"/>
</div>
<script src="miam.js"></script>
</body>
</html>
miam.js
// Vide pour l'instant
miam.css
#terrain {
position: relative;
width: 800px;
height: 800px;
background-image: url(herbe.jpg);
}
#chat {
position: absolute;
top: 0px;
left: 0px;
transition: top 1s, left 1s;
}
Créez deux variables « terrain » et « chat » contenant les éléments DOM ayant les id « terrain » et « chat ».
Quand l'utilisateur clique sur le terrain (et uniquement le terrain, ou ce qui est contenu dedans), affichez « click » dans la console.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(){
console.log('click');
});
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',XYZ);
Que faut-il écrire à la place de XYZ pour afficher dans la console la coordonnée X de l'endroit sur la page où l'utilisateur à cliqué ?
N'utilisez pas de fonction fléchée, et utilisez les noms habituels.
Le début de votre réponse est correct:
function(event){
Le problème est après
Votre réponse contient « pageX », ce qui est correct.
Votre réponse commence par:
function (){
Il manque quelque chose.
Indice: Comment récupère-t-on les informations sur le click ?
Ce pauvre chat s'ennuie. Il a envie de bouger!
pageX et pageY donnent des coordonnées par rapport au coin en haut à gauche de la page.
Ici on va utiliser offsetX et offsetY qui donnent des coordonnées par rapport au coin en haut à gauche du terrain.
Supposons qu'on ait déjà :
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(event){
let x=event.offsetX;
XYZ
});
Que faut-il écrire à la place de XYZ pour déplacer la position du chat à l'endroit cliqué ?
(juste pour la position X)
N'oubliez pas les unités. Utilisez les variables définies.
Essayez dans votre code d'abord.
Vous êtes sur la bonne voie. La réponse commence bien par:
chat.style.left ...
Le problème est après.
Vous êtes sur la bonne voie. La réponse commence bien par:
chat.style ...
Le problème est après.
Complétez le programme pour que le chat se déplace à l'endroit cliqué à la fois sur x et y.
Utilisez bien une variable "x" et "y" pour la position du chat.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(event){
console.log('click');
let x=event.offsetX;
let y=event.offsetY;
chat.style.left=x+'px';
chat.style.top =y+'px';
});
Le chat est content de bouger, mais maintenant, il n’arrête pas de sortir de son terrain. En plus, il ne vas pas exactement à l'endroit cliqué.
Important: Mettez ce code dans votre gestionnaire d'événements:
(si vous le mettez en dehors, il s’exécutera au chargement, et les valeurs seront à 0)
const rectChat = chat.getBoundingClientRect();
rectChat.width et rectChat.height vous donnent la largeur et la hauteur de l'image du chat.
En les utilisant,
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(event){
console.log('click');
const rectChat = chat.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}
chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';
});
Le chat se sent seul sur son terrain.
Ajoutons une souris.
Créez le fichier
Ajoutez là dans le HTML après le chat, avec un id "souris".
Copiez le CSS du chat et adaptez.
Dans ce fichier CSS faites que la souris commence à top: 500px; left: 600px;
En dupliquant différents bout de code, faites en sorte que, quand vous cliquez quelque-part dans le terrain, le chat et la souris se placent au même endroit !
Le chat et la souris sont devenus inséparables!
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(event){
console.log('click');
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}
if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}
chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';
souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});
La souris se sent mal à l'aise. Il y-a peut-être quelque-chose dans le regard ou dans le sourire de son ami qui l'inquiète un peu. Elle voudrait plus d'indépendance.
On voudrait donc que deux utilisateurs différents contrôlent le chat et la souris. Chacun sur son navigateur.
Les clicks d'un utilisateur indiqueraient la position du chat et les clicks de l'autre utilisateur indiqueraient la position de la souris.
Il faudra donc échanger des informations entre les deux navigateurs. On va le faire, en passant par un serveur, avec des requêtes AJAX.
Quand l'utilisateur chat clique, les coordonnées de son click sont envoyées (AJAX) à miam-serveur.php.
miam-serveur.php va attendre de recevoir les coordonnées de la souris avant de répondre.
La réponse contiendra à la fois les coordonnées cliquées par l'utilisateur chat et celles cliquées par l'utilisateur souris.
Récapitulons:
Les promesses ont une valeur (du moins en JS).
Une promesse représente une opération future, qui peut renvoyer une valeur. C'est la « valeur » de la promesse.
Par exemple, lorsqu'on fait une requête AJAX avec simple_fetch, on veut pouvoir récupérer la réponse du serveur.
simple_fetch() renvoie une promesse. La réponse du serveur sera la « valeur » de la promesse. Cette valeur n'est pas disponible au départ. Elle n'est disponible que lorsque la promesse est tenue. On ne peut récupérer la valeur qu'après que la promesse soit tenue, c'est à dire dans la fonction donnée à .then() ou bien après « await ».
Les deux manières de récupérer:
1. En paramètre de la fonction:
simple_fetch('http://exemple.org/abc.php').then( (valeur)=>{console.log(valeur);});
2. Lorsqu'on utilise « await »
let valeur= await simple_fetch('http://exemple.org/abc.php');
console.log(valeur);
Créez le fichier miam-serveur.php dans ~/public_html
<?php
ini_set("xdebug.overload_var_dump", "off");
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
$data=lire_fichier_data();
if(isset($_GET['clickChatX']) &&
isset($_GET['clickChatY'])){
$data['clickChat']=['x'=>floatval($_GET['clickChatX']),
'y'=>floatval($_GET['clickChatY'])];
}
if(isset($_GET['clickSourisX']) &&
isset($_GET['clickSourisY'])){
$data['clickSouris']=['x'=>floatval($_GET['clickSourisX']),
'y'=>floatval($_GET['clickSourisY'])];
}
if(isset($data['clickChat']) || isset($data['clickSouris'])){
ecrire_fichier_data($data);
}
if(!isset($data['clickChat']) && !isset($data['clickSouris'])){
erreur('Pas de click fourni.');
}
// Attendre que l'autre joueur clique et que le processus PHP correspondant écrive les autres coordonnées dans le fichier
$maxAttente=500;
for($i=0;isset($data['clickChat']) !== isset($data['clickSouris']) && $i<$maxAttente;$i++){
usleep(100*1000);
$data=lire_fichier_data();
}
$output=$data;
// Cas particulier. Si l'utilisateur a cliqué plusieurs fois, certains des processus en attente risquent de voir un fichier déjà vidé.
if(!isset($data['clickChat']) && !isset($data['clickSouris'])){
$output=[];
}
// Timeout.
if($i===$maxAttente){
erreur('Delai d\'attente pour l\'autre click dépasé.');
}
unset($data['clickChat' ]);
unset($data['clickSouris']);
// Attendre pour que l'autre processus ait le temps de lire, avant qu'on éfface.
// Pas très joli, mais efficace.
usleep(150*1000);
ecrire_fichier_data($data);
echo json_encode($output);
function lire_fichier_data(){
$fichier=@file_get_contents('data.json');
if($fichier===false){$fichier='[]';}
$res=json_decode($fichier, JSON_OBJECT_AS_ARRAY);
if($res===null){$res=[];}
return $res;
}
function ecrire_fichier_data($data){
$ok=@file_put_contents('data.json',json_encode($data));
if(!$ok){
erreur('Problème d\'écriture dans data.json. Avez-vous crée le fichier et donné les droits requis ?');
}
}
function erreur($message){
http_response_code(500);
echo json_encode(['erreur'=>$message]);
die(1);
}
// Debugger (il faut d'abord créer un fichier "log" avec les droits d'écriture)
function log_dump(...$args)
{
ob_start();
var_dump(...$args);
$content = ob_get_contents();
ob_end_clean();
file_put_contents('log','######'.date('r').':'.getmypid().': '.$content,FILE_APPEND);
}
?>
En PHP, comment s’appelle la variable qui permet de récupérer les paramètres qui se trouvent dans l'URL ?
On demande juste son nom.
Presque! Il manque un caractère dans son nom.
Vous êtes sur la bonne voie. Il y a plusieurs problèmes. On veut le nom précis de la variable.
Presque!
En PHP, la variable globale $_GET permet de récuperer les paramètres de l'URL.
Essayons de découvrir dans le code PHP, quels paramètres attend miam-serveur.php.
Regardez rapidement le début du code.
Donnez un seul des noms de paramètre attendus.
Vous êtes sur la bonne voie. On demande un seul des noms
Presque! Il y a des choses en trop.
Ouvrez miam-serveur.php dans votre navigateur:
http://localhost/~__user_nb__/miam-serveur.php
Vous devriez vois une erreur : « Pas de click fourni. »
Maintenant, dans la barre d'adresses de votre navigateur, ajoutez à la fin de cette URL les paramètres GET pour le chat (en inventant des valeurs)
Quand vous avez trouvé la bonne URL, vous devriez avoir une erreur différente:
« Problème d'écriture dans data.json. Avez-vous crée le fichier et donné les droits requis ? »
Écrivez votre URL ci-dessous:
Vous êtes sur la bonne voie. La réponse commence bien par:
http://localhost/(...)miam-serveur.php?clickChat ...
Le problème est après.
Votre réponse devrait être une URL du type:
http://localhost/...
Vous avez écrit " && ". Vous ne devez mettre qu'un seul "&"
Pour fonctionner, miam-serveur.php a besoin d'écrire dans un fichier appelé « data.json »
Dans vote terminal, allez dans le répertoire de miam-serveur.php (normalement ~/public_html) et créez le fichier vide avec : « touch data.json »
miam-serveur.php est executé par le serveur web (Apache). Le serveur web a un utilisateur différent que vous.
Pour que le serveur web puisse écrire dans ce fichier, il faut permettre aux autres utilisateurs d'écrire dans data.json
Quelle commande faut-il taper pour changer les droits sur ce fichier ?
Vous êtes sur la bonne voie. C'est bien :
chmod ... data.json
Reste à déterminer « ... ».
Non, pas besoin de sudo ici.
Vous n'avez pas indiqué le nom de fichier pour chmod.
(ou si vous en avez indiqué un, ce n 'est pas le bon).
Vous êtes sur la bonne voie.
Il s'agit bien de la commande chmod.
Maintenant, essayez l'URL à nouveau:
http://localhost/~__user_nb__/miam-serveur.php?clickChatX=123&clickChatY=123
Au lieu d'avoir un message d'erreur, la requête devrait attendre.
Laissez là attendre et ouvrez un autre onglet avec le click de la souris:
http://localhost/~__user_nb__/miam-serveur.php?clickSourisX=222&clickSourisY=222
Dès que la 2e requête se lance, les deux requêtes devraient aboutir et retourner les deux coordonnées.
Rappel:
Ouvrez deux onglets avec le fichier miam.html
Ouvrez la console dans les deux.
Que faut-il taper pour faire la requête du chat avec simple_fetch ?
Essayez dans la console avant de répondre.
Inventez des valeurs pour les coordonnées.
Indice: Votre réponse devrait commencer par « simple_fetch »
L'URL de votre requête devrait commencer par :
http://localhost/(...)miam-serveur.php
Où (...) dépend de votre configuration.
Vous êtes sur la bonne voie. La réponse commence bien par:
simple_fetch('http://localhost/.../miam-serveur.php?
Le problème est après.
Oui, c'est vrai, on pourrait utiliser {get:{...}}
Ici, pour faire plus clair, on veut spécifier les paramètres GET dans l'URL directement.
Donc dans la console de chaque onglet , il fallait faire quelque chose du type:
simple_fetch('http://localhost/~__user_nb__/miam-serveur.php?clickChatX=123&clickChatY=123');
simple_fetch('http://localhost/~__user_nb__/miam-serveur.php?clickSourisX=321&clickSourisY=321');
Il faut que chaque utilisateur choisisse si il veut le chat ou la souris.
Ajoutez un select dans le HTML, avant le terrain:
<select id="joueur">
<option value="chat">Chat</option>
<option value="souris">Souris</option>
</select>
A l'intérieur du gestionnaire d’événements click, déterminez le joueur. Vous pouvez utiliser la propriété « .value » comme pour un champs texte.
Affichez le joueur dans la console.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}
if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}
chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';
souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});
Dans le gestionnaire d'événements, on va vouloir utiliser simple_fetch avec « await » pour faire la requete AJAX.
Pour pouvoir utiliser « await », que faut-il écrire à la place de XYZ ?
terrain.addEventListener('click',XYZ function(event){
On veut ajouter la requete simple_fetch dans le code JS.
Pour ça:
Pour tester, vous devez cliquer dans deux onglets différents, l'un chat, l'autre souris.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',async function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}
if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}
let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}
reponse=await simple_fetch(url);
console.log(reponse);
chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';
souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});
On y est presque !
Regardez bien la réponse du serveur. C'est un objet, contenant deux objets.
Utilisez ces informations à la place de x et y pour mettre à jour les coordonnées du chat et de la souris.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
terrain.addEventListener('click',async function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}
if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}
let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}
reponse=await simple_fetch(url);
console.log(reponse);
chat.style.left=(reponse.clickChat.x-rectChat.width/2)+'px';
chat.style.top =(reponse.clickChat.y-rectChat.height/2)+'px';
souris.style.left=(reponse.clickSouris.x-rectSouris.width/2)+'px';
souris.style.top =(reponse.clickSouris.y-rectSouris.height/2)+'px';
});
Ca y'est ! Notre souris est enfin indépendante. Ouf, il était temps. Elle trouvait que le regard du chat devenait insistant.
Il nous reste un petit soucis. Entre le chat et la souris, il peut parfois y avoir des problèmes.
Dans notre code aussi... des problèmes comme de promesses non tenues.
let reponse;
try{
reponse=await simple_fetch(url);
}catch(e){
console.log('erreur',e);
alert(e.erreur);
return;
}
La souris avait raison de se méfier. Ce chat a un petit creux.
Cette partie est juste pour s'amuser.
Si vous êtes en retard (ou si êtes trop angoissés par ce qu'il pourrait arriver à la souris), vous pouvez passer.
On veut déterminer si le chat attrape la souris. Pour ça, on va faire des vérifications toutes les 50 millisecondes.
setInterval, qu'on a déjà utilisé, permet d'appeler une fonction régulièrement.
En utilisant:
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
determinez les conditions pour que ces deux rectangles se touchent.
Affichez alors « Miam » dans un alert.
setInterval(()=>{
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
//console.log(rectChat);
if(rectChat.right > rectSouris.left &&
rectChat.left < rectSouris.right &&
rectChat.bottom > rectSouris.top &&
rectChat.top < rectSouris.bottom )
{
alert('Miam');
}
},50);
On veut que le chat et la souris retournent au point de départ après.
Effacez les valeurs top, left des deux (chat et souris) en utilisant « null ».
Le retour pose problème. D'autres collisions chat-souris vont survenir. Pour les éviter, créez une variable « partieEnCours ».
Si la partie n'est pas en cours, ne faites pas de vérification de collision.
Après avoir affiché « Miam », mettez partieEnCours à false et remettez la à true après 1200ms (setTimeout).
const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
const souris =document.getElementById('souris');
let partieEnCours=true;
terrain.addEventListener('click',async function(e){
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;
let rect;
if(joueur==='chat'){rect= chat.getBoundingClientRect();}
else {rect=souris.getBoundingClientRect();}
if(x< rect.width/2 ){x= rect.width/2;}
if(x>800-rect.width/2 ){x=800-rect.width/2;}
if(y< rect.height/2){y= rect.height/2;}
if(y>600-rect.height/2){y=600-rect.height/2;}
let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}
let reponse;
try{
reponse=await simple_fetch(url);
}catch(e){
console.log('erreur',e);
alert(e.erreur);
return;
}
chat.style.left=(reponse.clickChat.x-rectChat.width/2)+'px';
chat.style.top =(reponse.clickChat.y-rectChat.height/2)+'px';
souris.style.left=(reponse.clickSouris.x-rectSouris.width/2)+'px';
souris.style.top =(reponse.clickSouris.y-rectSouris.height/2)+'px';
});
setInterval(()=>{
if(partieEnCours===false){return;}
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
//console.log(rectChat);
if(rectChat.right > rectSouris.left &&
rectChat.left < rectSouris.right &&
rectChat.bottom > rectSouris.top &&
rectChat.top < rectSouris.bottom )
{
alert('Miam');
partieEnCours=false;
setTimeout(()=>partieEnCours=true,1200);
chat.style.left=null;
chat.style.top =null;
souris.style.left=null;
souris.style.top =null;
}
},50);