From e26d24940b2234a1a5e63b19d19d298bf36354e2 Mon Sep 17 00:00:00 2001 From: julieng Date: Sun, 14 Nov 2021 14:30:32 +0100 Subject: move *.html to *.md --- .../client-side_storage/index.html | 882 -------------------- .../client-side_storage/index.md | 882 ++++++++++++++++++++ .../drawing_graphics/index.html | 923 --------------------- .../client-side_web_apis/drawing_graphics/index.md | 923 +++++++++++++++++++++ .../client-side_web_apis/fetching_data/index.html | 398 --------- .../client-side_web_apis/fetching_data/index.md | 398 +++++++++ .../javascript/client-side_web_apis/index.html | 52 -- .../learn/javascript/client-side_web_apis/index.md | 52 ++ .../client-side_web_apis/introduction/index.html | 297 ------- .../client-side_web_apis/introduction/index.md | 297 +++++++ .../manipulating_documents/index.html | 332 -------- .../manipulating_documents/index.md | 332 ++++++++ .../third_party_apis/index.html | 441 ---------- .../client-side_web_apis/third_party_apis/index.md | 441 ++++++++++ .../video_and_audio_apis/index.html | 519 ------------ .../video_and_audio_apis/index.md | 519 ++++++++++++ 16 files changed, 3844 insertions(+), 3844 deletions(-) delete mode 100644 files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/fetching_data/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/fetching_data/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/introduction/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/introduction/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.md delete mode 100644 files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html create mode 100644 files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.md (limited to 'files/fr/learn/javascript/client-side_web_apis') diff --git a/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.html b/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.html deleted file mode 100644 index 60cc11cd4a..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.html +++ /dev/null @@ -1,882 +0,0 @@ ---- -title: Stockage côté client -slug: Learn/JavaScript/Client-side_web_APIs/Client-side_storage -tags: - - API - - Apprendre - - Codage - - Débutant - - Guide - - IndexedDB - - JavaScript - - Storage -translation_of: Learn/JavaScript/Client-side_web_APIs/Client-side_storage -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Client-side_storage ---- -

{{LearnSidebar}}

- -
{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
- -
- -

Les navigateurs web modernes permettent aux sites web de stocker des données sur l'ordinateur de l'utilisateur — avec sa permission — puis de les récupérer au besoin. Cela permet d'enregistrer des données pour du stockage à long terme, de sauvegarder des documents ou des sites hors-ligne, de conserver des préférences spécifiques à l'utilisateur et plus encore. Cet article explique les fondamentaux pour y parvenir.

- - - - - - - - - - - - -
Prérequis:Notions de bases de JavaScript (voir premiers pas, les briques JavaScript, les objets JavaScript), les notions de base des APIs côté client
Objectif:Apprendre à utiliser les APIs de stockage côté client pour stocker des données de l'application.
- -

Stockage côté client ?

- -

Ailleurs dans la zone d'apprentissage de MDN, nous avons parlé de la différence entre les sites statiques et les sites dynamiques — ces derniers stockent des données côté serveur en utilisant une base de données. Ensuite, ils exécutent du code pour récupérer les données et les insérer dans des templates de page statique. Finalement, le HTML résultant est envoyé au client, qui est alors affiché par le navigateur de l'utilisateur.

- -

Le stockage côté client fonctionne sur des principes similaires, mais pour une utilisation différente. Le stockage côté client repose sur des APIs JavaScript qui permettent de stocker des données sur la machine de l'utilisateur et de les récupérer au besoin. Cela peut se révéler utile dans différents cas comme :

- - - -

Souvent, le stockage côté client et côté serveur sont utilisés ensemble. Par exemple, vous pouvez télécharger à partir d'une base de données côté serveur une série de fichiers mp3 utilisés par un site web (comme un jeu ou une application de musique) vers une base de données côté client et ainsi pouvoir les lire quand vous le voulez. Avec cette stratégie, l'utilisateur n'a à télécharger le fichier qu'une seule fois — les visites suivantes, ils sont récupérés à partir de la base de données locale.

- -
-

Note : La quantité de données que l'on peut stocker à l'aide des APIs de stockage côté client est limitée (limite par API et limite globale), la limite exacte dépend du navigateur et des configurations. Voir Limites de stockage du navigateur et critères d'éviction pour plus d'informations.

-
- -

À l'ancienne : les cookies

- -

Le concept de stockage côté client existe depuis longtemps. Au début du web, les sites utilisaient des cookies pour stocker des informations et personnaliser l'expérience utilisateur. C'est la méthode de stockage côté client la plus couramment utilisée et la plus ancienne.

- -

De par leur histoire, les cookies souffrent d'un certain nombre de problèmes — tant techniques qu'au niveau de l'expérience utilisateur. Ces problèmes sont suffisamment importants pour imposer un message d'information aux utilisateurs habitant en Europe lors de leur première visite si le site utilise des cookies pour stocker des informations sur eux. Cela est dû à une loi de l'Union Européenne connue sous le nom de directive Cookie.

- -

- -

Pour ces raisons, nous ne verrons pas dans cet article comment utiliser les cookies. Entre le fait qu'ils sont dépassés, les problèmes de sécurité qu'ils présentent et l'incapacité de stocker des données complexes, les cookies ne sont pas la meilleure manière pour stocker des données. Il y a de meilleures alternatives, modernes, permettant de stocker des données variées sur l'ordinateur de l'utilisateur.

- -

Le seul avantage des cookies est qu'ils sont supportés par des navigateurs anciens : si votre projet requiert le support de navigateurs obsolètes (comme Internet Explorer 8 et inférieur), les cookies peuvent se révéler utiles. Pour la plupart des projets, vous ne devriez pas avoir besoin d'y recourir.

- -
-

Note : Pourquoi existe-t-il encore de nouveaux sites crées à l'aide de cookies? Principalement de par les habitudes des développeurs, l'utilisation de bibliothèques anciennes qui utilisent encore des cookies et l'existence de nombreux sites web fournissant des formations et références dépassées pour apprendre à stocker des données.

-
- -

La nouvelle école : Web Storage et IndexedDB

- -

Les navigateurs modernes ont des APIs beaucoup plus efficaces et faciles d'utilisation pour stocker des données côté client.

- - - -

Vous en apprendrez plus sur ces APIs ci-dessous.

- -

Le futur : l'API Cache

- -

Certains navigateurs modernes prennent en charge la nouvelle API {{domxref("Cache")}}. Cette API a été conçue pour stocker les réponses HTTP de requêtes données et est très utile pour stocker des ressources du site afin qu'il soit accessible sans connexion réseau par exemple. Le cache est généralement utilisé avec l'API Service Worker, mais ce n'est pas obligatoire.

- -

L'utilisation du Cache et des Service Workers est un sujet avancé, nous ne le traiterons pas en détail dans cet article, nous ne montrerons qu'un simple exemple dans la section {{anch("Stockage hors-ligne de ressources")}} plus bas.

- -

Stocker des données simples — web storage

- -

L'API Web Storage est très facile à utiliser — on stocke une simple paire clé/valeur de données (limité aux données scalaires) et on les récupére au besoin.

- -

Syntaxe basique

- -

Nous allons vous guider pas à pas :

- -
    -
  1. -

    Tout d'abord, ouvez notre template vide de web storage sur GitHub dans un nouvel onglet.

    -
  2. -
  3. -

    Ouvrez la console JavaScript de votre navigateur.

    -
  4. -
  5. -

    Toutes les données du web storage sont contenues dans deux structures de type objet : {{domxref("Window.sessionStorage", "sessionStorage")}} et {{domxref("Window.localStorage", "localStorage")}}. Le premier conserve les données aussi longtemps que le navigateur est ouvert (elles sont perdues lorsque le navigateur est fermé) et le second conserve les données même après que le navigateur ait été fermé puis ré-ouvert. Nous allons utiliser le second dans cet article car il est généralement plus utile.

    - -

    La méthode {{domxref("Storage.setItem()")}} permet de sauvegarder des données dans le storage — elle prend deux paramètres : le nom de l'entrée à enregistrer et sa valeur. Essayez de taper ce qui suit dans votre console JavaScript (changez le nom et la valeur si vous le voulez !) :

    - -
    localStorage.setItem('name','Chris');
    -
  6. -
  7. -

    La méthode {{domxref("Storage.getItem()")}} prend un paramètre — le nom de l'entrée que vous voulez récupérer — et retourne la valeur de l'entrée. Maintenant, tapez ces lignes dans votre console JavaScript :

    - -
    var myName = localStorage.getItem('name');
    -myName
    - -

    En tapant la deuxième ligne, vous devriez voir que la variable myName contient la valeur de l'entrée name.

    -
  8. -
  9. -

    La méthode {{domxref("Storage.removeItem()")}} prend un paramètre — le nom de l'entrée de vous voulez supprimer — et supprime l'entrée du web storage. Tapez les lignes suivantes dans votre console JavaScript :

    - -
    localStorage.removeItem('name');
    -var myName = localStorage.getItem('name');
    -myName
    - -

    La troisième ligne devrait maintenant retourner null — l'entrée name n'existe plus dans le web storage.

    -
  10. -
- -

Les données persistent !

- -

Une caractéristique clé du web storage est que les données persistent entre les différents chargements de page (et même lorsque le navigateur est arrêté dans le cas du localStorage). Regardons ça en action :

- -
    -
  1. -

    Ouvrez notre template vide une fois de plus, mais cette fois dans un navigateur différent de celui dans lequel vous avez ouvert ce tutoriel. Cela rendra la suite plus facile.

    -
  2. -
  3. -

    Tapez ces lignes dans la console JavaScript du navigateur que vous venez d'ouvrir :

    - -
    localStorage.setItem('name','Chris');
    -var myName = localStorage.getItem('name');
    -myName
    - -

    Vous devriez voir que l'entrée name est bien là.

    -
  4. -
  5. -

    Maintenant, fermez le navigateur et ouvrez-le de nouveau.

    -
  6. -
  7. -

    Entrez les lignes suivantes :

    - -
    var myName = localStorage.getItem('name');
    -myName
    - -

    Vous devriez voir que la valeur est toujours accessible, quand bien même le navigateur a été redémarré.

    -
  8. -
- -

Stockage séparé pour chaque domaine

- -

Il existe un système de stockage distinct pour chaque domaine (chaque adresse web chargée dans le navigateur a accès à son propre storage et pas aux autres). Vous verrez que si vous chargez deux sites web (disons google.com et amazon.com) et essayez de stocker un élément, il ne sera pas disponible sur l'autre site.

- -

C'est plutôt logique — imaginez les problèmes de sécurité qui se poseraient si les sites web pouvaient voir les données d'un autre !

- -

Un exemple plus impliqué

- -

Appliquons cette nouvelle connaissance pour écrire un exemple, cela vous donnera une idée de la façon dont le web storage peut être utilisé. Notre exemple permettra d'envoyer un nom, à la suite de quoi la page sera mise à jour pour donner un accueil personnalisé. Cet état persistera également après un rechargement de la page ou redémarrage du navigateur, puisqu'il sera stocké dans le web storage.

- -

Le HTML de l'exemple est disponible à personal-greeting.html — il s'agit d'un site web très simple avec entête, contenu et pied de page, ainsi qu'un formulaire pour entrer votre nom.

- -

- -

Nous allons construire cet exemple pas à pas, cela vous permettra de comprendre comment ça marche.

- -
    -
  1. -

    D'abord, copiez notre fichier personal-greeting.html dans un nouveau répertoire sur votre ordinateur.

    -
  2. -
  3. -

    Ensuite, créez un fichier index.js dans le même répertoire que le fichier HTML — le fichier HTML inclut ce script (voir ligne 40).

    -
  4. -
  5. -

    Nous allons commencer par récupérer les références de tous les éléments HTML qu'on manipulera dans cet exemple — nous les créons en tant que constantes car ces références n'ont pas besoin d'être modifiées au cours de l'exécution de l'application. Ajoutez les lignes suivantes à votre fichier JavaScript:

    - -
    // créer les constantes nécessaires
    -const rememberDiv = document.querySelector('.remember');
    -const forgetDiv = document.querySelector('.forget');
    -const form = document.querySelector('form');
    -const nameInput = document.querySelector('#entername');
    -const submitBtn = document.querySelector('#submitname');
    -const forgetBtn = document.querySelector('#forgetname');
    -
    -const h1 = document.querySelector('h1');
    -const personalGreeting = document.querySelector('.personal-greeting');
    -
  6. -
  7. -

    Ensuite, on doit ajouter un gestionnaire d'événement pour empêcher le formulaire d'être véritablement soumis lorsque le bouton de soumission est cliqué, puisque ce n'est pas le comportement que l'on veut. Ajoutez le bout de code suivant à la suite de du code précédent :

    - -
    // Empêcher le form d'être soumis
    -form.addEventListener('submit', function(e) {
    -  e.preventDefault();
    -});
    -
  8. -
  9. -

    Maintenant, on doit ajouter un gestionnaire d'événement pour gérer le clic sur le bouton "Say hello" (dire bonjour). Les commentaires expliquent ce que chaque instruction fait, mais, en substance, on prend le nom que l'utilisateur a entré dans le champs texte et on l'enregistre dans le web storage avec setItem(). Ensuite, on exécute une fonction appelée nameDisplayCheck() qui se charge de mettre à jour le contenu du site web. Ajoutez ceci au bas de votre code :

    - -
    // exécuter la fonction quand le bouton 'Say hello' est cliqué
    -submitBtn.addEventListener('click', function() {
    -  // stocker le nom entré dans le web storage
    -  localStorage.setItem('name', nameInput.value);
    -  // exécuter nameDisplayCheck() pour afficher la
    -  // page personnalisée et changer le formulaire
    -  nameDisplayCheck();
    -});
    -
  10. -
  11. -

    On doit maintenant gérer l'événement lorsque le bouton "Forget" (oublier) est cliqué — il est affiché une fois que le bouton "Say hello" a été cliqué (les deux boutons permettent de basculer d'un état à l'autre). Dans cette fonction, on supprime l'élément name du web storage en utilisant removeItem(), puis on exécute nameDisplayCheck() pour mettre à jour l'affichage. Ajoutez ceci au bas de votre code :

    - -
    // exécuter la fonction quand le bouton 'Forget' est cliqué
    -forgetBtn.addEventListener('click', function() {
    -  // supprimer l'item name du web storage
    -  localStorage.removeItem('name');
    - // exécuter nameDisplayCheck() pour afficher la
    - // page personnalisée et changer le formulaire
    -  nameDisplayCheck();
    -});
    -
  12. -
  13. -

    Il est maintenant temps de définir la fonction nameDisplayCheck() elle-même. Ici, on vérifie si l'élément name est stocké dans le web storage en utilisant localStorage.getItem('name') comme condition. S'il existe, la valeur retournée sera évaluée à true; sinon, comme false. S'il existe, on affiche un message d'accueil personnalisé et le bouton "Forget" du formulaire, tout en masquant le bouton "Say hello" du formulaire. Sinon, on affiche un message d'accueil générique et le bouton "Say hello". Encore une fois, mettez les lignes suivantes au bas de votre code :

    - -
    // définit la fonction nameDisplayCheck()
    -function nameDisplayCheck() {
    -  // vérifie si l'élément 'name' est stocké dans le web storage
    -  if(localStorage.getItem('name')) {
    -    // Si c'est le cas, affiche un accueil personnalisé
    -    let name = localStorage.getItem('name');
    -    h1.textContent = 'Welcome, ' + name;
    -    personalGreeting.textContent = 'Welcome to our website, ' + name + '! We hope you have fun while you are here.';
    -    // cache la partie 'remember' du formulaire et affiche la partie 'forget'
    -    forgetDiv.style.display = 'block';
    -    rememberDiv.style.display = 'none';
    -  } else {
    -    // Sinon, affiche un accueil générique
    -    h1.textContent = 'Welcome to our website ';
    -    personalGreeting.textContent = 'Welcome to our website. We hope you have fun while you are here.';
    -    // cache la partie 'forget' du formulaire et affiche la partie 'remember'
    -    forgetDiv.style.display = 'none';
    -    rememberDiv.style.display = 'block';
    -  }
    -}
    -
  14. -
  15. -

    Dernier point, mais non des moindres, on exécute la fonction nameDisplayCheck() à chaque fois que la page est chargée. Si on ne le faisait pas, l'accueil personnalisé ne serait pas affiché après qu'on ait rafraichit la page. Ajoutez ce qui suit au bas de votre code :

    - -
    document.body.onload = nameDisplayCheck;
    -
  16. -
- -

Notre exemple est terminé — bien joué ! Il ne vous reste plus qu'à enregistrer votre code et tester votre page HTML dans un navigateur. Vous pouvez voir notre version terminée en direct ici (ou le code JavaScript terminé).

- -
-

Note : Vous pouvez trouver un exemple un peu plus complexe dans l'article Utiliser l'API de stockage web.

-
- -
-

Note : Dans la ligne <script src="index.js" defer></script> de notre version finie, l'attribut defer spécifie que le contenu de l'élément {{htmlelement("script")}} ne doit pas s'exécuter avant que la page ait fini de charger.

-
- -

Stocker des données complexes — IndexedDB

- -

L'API IndexedDB (parfois abrégé IDB) est un système de base de données complet disponible dans le navigateur. Vous pouvez y stocker des données complexes, les types ne sont pas limités à des valeurs simples de type chaînes ou nombres. Vous pouvez stocker des vidéos, des images et à peu près tout ce que vous voulez, dans une instance IndexedDB.

- -

Cependant, cela a un coût : IndexedDB est beaucoup plus complexe à utiliser que l'API Web Storage. Dans cette section, nous ne ferons qu'égratigner la surface de ce qu'IndexedDB peut faire, mais nous vous en donnerons assez pour débuter.

- -

Un exemple de stockage de notes

- -

Nous allons voir un exemple qui vous permettra de stocker des notes dans votre navigateur, les voir et les supprimer, quand vous le souhaitez. Vous apprendrez à le construire par vous-même au fur et à mesure des explications et cela vous permettra de comprendre les parties fondamentales d'IDB.

- -

L'application ressemble à ceci :

- -

- -

Chaque note a un titre et une description, chacun éditables individuellement. Le code JavaScript que nous allons voir ci-dessous contient des commentaires détaillés pour vous aider à comprendre ce qu'il se passe.

- -

Pour commencer

- -
    -
  1. Tout d'abord, copiez les fichiers index.html, style.css, et index-start.js dans un nouveau répertoire sur votre ordinateur.
  2. -
  3. Jetez un coup d'oeil aux fichiers. -
      -
    • Vous verrez que le HTML est assez simple : un site web avec une entête et un pied de page, ainsi qu'une zone de contenu principal contenant un emplacement pour afficher les notes et un formulaire pour en ajouter.
    • -
    • Le CSS fournit un style simple pour rendre plus clair ce qu'il se passe.
    • -
    • Le fichier JavaScript contient cinq constantes déclarées — des références à l'élément {{htmlelement("ul")}} dans lequel seront affichées les notes, les {{htmlelement("input")}} title et body, le {{htmlelement("form")}} lui-même, et un {{htmlelement("button")}}.
    • -
    -
  4. -
  5. Renommez votre fichier JavaScript en index.js. Vous êtes maintenant prêt pour y ajouter du code.
  6. -
- -

Configuration initiale de la base de données

- -

Voyons maintenant la première chose à faire, mettre en place la base de données.

- -
    -
  1. -

    À la suite des déclarations de constantes, ajoutez les lignes suivantes :

    - -
    // Objet db pour stocker la BDD ouverte
    -let db;
    - -

    Ici, on déclare une variable appelée db — on l'utilisera plus tard pour stocker un objet permettant d'accéder à la base de données. On l'utilisera à plusieurs endroits, on l'a donc déclaré globablement ici pour faciliter les choses.

    -
  2. -
  3. -

    Ensuite, ajoutez ce qui suit au bas de votre code :

    - -
    window.onload = function() {
    -
    -};
    - -

    On écrira tout notre code dans le gestionnaire d'événement window.onload, appelé quand l'événement {{event("load")}} de la fenêtre est chargé, pour s'assurer qu'on n'essaiera pas d'utiliser IndexedDB avant que l'application ne soit complètement chargée (ça ne marcherait pas sinon).

    -
  4. -
  5. -

    À l'intérieur de window.onload, ajoutez ce qui suit :

    - -
    // Ouvrir la BDD; elle sera créée si elle n'existe pas déjà
    -// (voir onupgradeneeded)
    -let request = window.indexedDB.open('notes', 1);
    - -

    Cette ligne crée une requête request pour ouvrir la version 1 de la base de données appelée notes. Si elle n'existe pas déjà, on devra la créer via un gestionnaire d'événement.

    - -

    Vous verrez très souvent ce format dans IndexedDB. Les opérations de base de données prennent du temps et on ne veut pas suspendre le navigateur le temps de récupérer le résultat, les opérations sur la base de données sont donc {{Glossary("asynchronous", "asynchrones")}} — ce qui signifie qu'au lieu d'arriver immédiatement, elles se produiront à un moment ultérieur et un événement sera déclenché lorsque cela arrivera.

    - -

    Pour gérer cela dans IndexedDB, on crée d'abord une requête (que vous pouvez appeler comme vous le voulez — on l'appelle request pour que ce soit plus explicite). On utilise ensuite des gestionnaire d'événement pour exécuter du code lorsque les requêtes sont terminées, échouent, etc, ce que l'on va voir ci-dessous.

    - -
    -

    Note : Le numéro de version est important. Si vous voulez mettre à jour votre base de données (par exemple, pour modifier la structure de la table), vous devez ré-exécuter votre code avec un numéro de version supérieur et spécifier le schéma de la base de données avec le gestionnaire d'événement onupgradeneeded. Nous ne verrons pas la mise à jour de base de données dans ce tutoriel.

    -
    -
  6. -
  7. -

    Maintenant, ajoutez les gestionnaires d'événement suivants, juste en dessous des lignes précédentes — toujours à l'intérieur de window.onload :

    - -
    // la base de données n'a pas pu être ouverte avec succès
    -request.onerror = function() {
    -  console.log('Database failed to open');
    -};
    -
    -// la base de données a été ouverte avec succès
    -request.onsuccess = function() {
    -  console.log('Database opened successfully');
    -
    -  // Stocke la base de données ouverte dans la variable db. On l'utilise par la suite
    -  db = request.result;
    -
    -  // Exécute la fonction displayData() pour afficher les notes qui sont dans la BDD
    -  displayData();
    -};
    - -

    Le gestionnaire d'événement {{domxref("IDBRequest.onerror", "request.onerror")}} s'exécutera si la requête échoue. Cela vous permet de gérer le problème si cela arrive. Dans notre exemple, on affiche simplement un message dans la console JavaScript.

    - -

    Le gestionnare d'événement {{domxref("IDBRequest.onsuccess", "request.onsuccess")}}, d'autre part, s'exécutera si la requête aboutit, que la base de données a été ouverte avec succès. Lorsque cela arrive, la propriété {{domxref("IDBRequest.result", "request.result")}} contient alors un objet représentant la base de données ouverte, qui nous permet de la manipuler. On stocke cette valeur dans la variable db qu'on a crée plus tôt pour pouvoir l'utiliser ensuite. On exécute également une fonction appelée displayData(), qu'on définira plus tard — elle affiche les données de la base de données dans le {{HTMLElement("ul")}}. On l'exécute dès à présent pour que les notes en base de données soient affichées dès que la page est chargée.

    -
  8. -
  9. -

    Pour en finir avec cette section, on ajoute le gestionnaire d'événement qui est  probablement le plus important, {{domxref("IDBOpenDBRequest.onupgradeneeded", "request.onupdateneeded")}}. Il est exécuté si la base de données n'a pas déjà été créée ou si on veut ouvrir la base de données avec un numéro de version supérieur à celle qui existe (pour faire une mise à jour). Ajoutez le code suivant en dessous de votre gestionnaire précédent :

    - -
    // Spécifie les tables de la BDD si ce n'est pas déjà pas fait
    -request.onupgradeneeded = function(e) {
    -  // Récupère une référence à la BDD ouverte
    -  let db = e.target.result;
    -
    -  // Crée un objectStore pour stocker nos notes (une table)
    -  // Avec un champ qui s'auto-incrémente comme clé
    -  let objectStore = db.createObjectStore('notes', { keyPath: 'id', autoIncrement:true });
    -
    -  // Définit les champs que l'objectStore contient
    -  objectStore.createIndex('title', 'title', { unique: false });
    -  objectStore.createIndex('body', 'body', { unique: false });
    -
    -  console.log('Database setup complete');
    -};
    - -

    C'est ici qu'on définit le schéma (la structure) de notre base de données; c'est à dire l'ensemble des champs (ou colonnes) qu'il contient.

    - -
      -
    1. -

      On récupère une référence à la base de données existante depuis e.target.result (la propriété result de la cible de l'événement, c'est à dire l'objet request). C'est l'équivalent de la ligne db = request.result; du gestionnaire d'événement onsuccess, mais on doit le faire de cette manière ici puisque le gestionnaire d'événement onupgradeneeded est exécuté avant onsuccess — la valeur de db n'est pas encore disponible.

      -
    2. -
    3. -

      Ensuite, on utilise {{domxref("IDBDatabase.createObjectStore()")}} pour créer un object store (un container pour une collection d'objets) à l'intérieur de notre base de données. C'est l'équivalent d'une table dans un système de base de données traditionnel. On lui a donné le nom notes, et un champs id avec autoIncrement — pour chaque nouvelle entrée dans cette table, une valeur auto-incrementée sera attributée au champ id sans que le développeur n'ait à le définir. Le champ id est la clé de l'object store: il sera utilisé pour identifier de manière unique les entrées, permettant de les mettre à jour ou les supprimer.

      -
    4. -
    5. -

      On crée deux autres index (champs) en utilisant la méthode {{domxref("IDBObjectStore.createIndex()")}}: title (qui contiendra le titre de chaque note), et body (qui contiendra la description de chaque note).

      -
    6. -
    -
  10. -
- -

Avec ce simple schéma de base de données en place, on va pouvoir ajouter des entrées à la base de données, des objets qui ressembleront à ça :

- -
{
-  title: "Acheter du lait",
-  body: "Lait de vache et de soja.",
-  id: 8
-}
- -

Ajouter des données à la base de données

- -

Maintenant, voyons comment ajouter des entrées dans la base de données. On le fera en utilisant le formulaire de notre page.

- -
    -
  1. -

    À la suite du gestionnaire d'événement précédent (mais toujours dans window.onload), ajoutez la ligne suivante — elle définit un gestionnaire d'événement onsubmit pour exécuter la fonction addData() quand le formulaire est soumis (que le {{htmlelement("button")}} envoyer est pressé et que les champs du formulaire sont valides) :

    - -
    // Créer un gestionnaire onsubmit pour appeler la fonction addData() quand le formulaire est soumis
    -form.onsubmit = addData;
    -
  2. -
  3. -

    Maintenant, définissons la fonction addData(). Ajoutez ce qui suit après la ligne précédente :

    - -
    // Définit la fonction addData()
    -function addData(e) {
    -  // empêcher le formulaire d'être soumis vers le serveur
    -  e.preventDefault();
    -
    -  // récupérer les valeurs entrées dans les champs du formulaire
    -  // et les stocker dans un objet qui sera inséré en BDD
    -  let newItem = { title: titleInput.value, body: bodyInput.value };
    -
    -  // ouvrir une transaction en lecture/écriture
    -  let transaction = db.transaction(['notes'], 'readwrite');
    -
    -  // récupérer l'object store de la base de données qui a été ouvert avec la transaction
    -  let objectStore = transaction.objectStore('notes');
    -
    -  // demander l'ajout de notre nouvel objet à l'object store
    -  var request = objectStore.add(newItem);
    -  request.onsuccess = function() {
    -    // vider le formulaire, pour qu'il soit prêt pour un nouvel ajout
    -    titleInput.value = '';
    -    bodyInput.value = '';
    -  };
    -
    -  // attendre la fin de la transaction, quand l'ajout a été effectué
    -  transaction.oncomplete = function() {
    -    console.log('Transaction completed: database modification finished.');
    -
    -    // mettre à jour l'affichage pour montrer le nouvel item en exécutant displayData()
    -    displayData();
    -  };
    -
    -  transaction.onerror = function() {
    -    console.log('Transaction not opened due to error');
    -  };
    -}
    - -

    C'est assez complexe, voyons ça pas à pas :

    - -
      -
    1. -

      On exécute {{domxref("Event.preventDefault()")}} sur l'objet événement pour empêcher le formulaire d'être véritablement soumis (cela provoquerait une actualisation de la page et gâcherait l'expérience utilisateur).

      -
    2. -
    3. -

      On crée un objet représentant une entrée à ajouter dans la base de données, en le remplissant avec les valeurs des champs du formulaire. Notez qu'on n'a pas besoin d'inclure explicitement une valeur id — comme nous l'avons précédemment expliqué, il est auto-rempli.

      -
    4. -
    5. -

      On ouvre une transaction en lecture/écritre (readwrite) sur l'object store notes en utilisant la méthode {{domxref("IDBDatabase.transaction()")}}. Cet object transaction va nous permettre d'accéder à l'object store, pour ajouter une nouvelle entrée par exemple.

      -
    6. -
    7. -

      On récupère l'object store de la transaction avec la méthode {{domxref("IDBTransaction.objectStore()")}} et on le stocke dans la variable objectStore.

      -
    8. -
    9. -

      On ajoute un nouvel enregistrement à la base de données en utilisant {{domxref("IDBObjectStore.add()")}}. Cela crée une requête, sur le même principe qu'on a déjà vu.

      -
    10. -
    11. On ajoute des gestionnaires d'événement à request et transaction pour exécuter du code aux points importants de leur cycle de vie : -
        -
      • Quand la requête a réussit, on efface les champs du formulaire — pour pouvoir ajouter une nouvelle note
      • -
      • Quand la transaction est terminé, on réexécute la fonction displayData() — pour mettre à jour l'affichage de notes sur la page.
      • -
      -
    12. -
    -
  4. -
- -

Afficher les données

- -

Nous avons déjà appelé displayData() deux fois dans notre code, nous allons maintenant définir cette fonction. Ajoutez ce qui suit à votre code, en dessous de la définition de la fonction précédente :

- -
// Définit la fonction displayData()
-function displayData() {
-  // Vide le contenu de la liste à chaque fois qu'on la met à jour
-  // Si on ne le faisait pas, des duplicats seraient affichés à chaque ajout
-  while (list.firstChild) {
-    list.removeChild(list.firstChild);
-  }
-
-  // Ouvre l'object store puis récupère un curseur - qui va nous permettre d'itérer
-  // sur les entrées de l'object store
-  let objectStore = db.transaction('notes').objectStore('notes');
-  objectStore.openCursor().onsuccess = function(e) {
-    // Récupère une référence au curseur
-    let cursor = e.target.result;
-
-    // S'il reste des entrées sur lesquelles itérer, on exécute ce code
-    if(cursor) {
-      // Crée un li, h3, et p pour mettre les données de l'entrée puis les ajouter à la liste
-      let listItem = document.createElement('li');
-      let h3 = document.createElement('h3');
-      let para = document.createElement('p');
-
-      listItem.appendChild(h3);
-      listItem.appendChild(para);
-      list.appendChild(listItem);
-
-      // Récupère les données à partir du curseur et les met dans le h3 et p
-      h3.textContent = cursor.value.title;
-      para.textContent = cursor.value.body;
-
-      // Met l'ID de l'entrée dans un attribut du li, pour savoir à quelle entrée il correspond
-      // Ce sera utile plus tard pour pouvoir supprimer des entrées
-      listItem.setAttribute('data-note-id', cursor.value.id);
-
-      // Crée un bouton et le place dans le li
-      let deleteBtn = document.createElement('button');
-      listItem.appendChild(deleteBtn);
-      deleteBtn.textContent = 'Delete';
-
-      // Définit un gestionnaire d'événement pour appeler deleteItem() quand le bouton supprimer est cliqué
-      deleteBtn.onclick = deleteItem;
-
-      // Continue l'itération vers la prochaine entrée du curseur
-      cursor.continue();
-    } else {
-      // Si la liste est vide, affiche un message "Aucune note n'existe"
-      if(!list.firstChild) {
-        let listItem = document.createElement('li');
-        listItem.textContent = 'No notes stored.';
-        list.appendChild(listItem);
-      }
-      // Il n'y a plus d'entrées dans le curseur
-      console.log('Notes all displayed');
-    }
-  };
-}
- -

Encore une fois, pas à pas :

- -
    -
  1. -

    D'abord on vide le contenu de l'élément {{htmlelement("ul")}}, pour pouvoir le remplir avec le contenu mis à jour. Si on ne le faisait pas, on obtiendrait une énorme liste de contenus dupliqués à chaque mise à jour.

    -
  2. -
  3. -

    Ensuite, on récupère une référence à l'object store notes en utilisant {{domxref("IDBDatabase.transaction()")}} et {{domxref("IDBTransaction.objectStore()")}} comme nous l'avons fait dans addData(), mais en chaînant ces deux instructions en une seule ligne.

    -
  4. -
  5. -

    L'étape suivante consiste à utiliser la méthode {{domxref("IDBObjectStore.openCursor()")}} pour ouvrir un curseur — une construction qui peut être utilisée pour itérer sur les entrées d'un object store. On chaîne un gestionnaire d'événement onsuccess à la fin de cette opération pour rendre le code plus concis — dès que le curseur est récupéré, le gestionnaire est exécuté.

    -
  6. -
  7. -

    On récupère une référence au curseur lui-même (un objet {{domxref("IDBCursor")}}) avec cursor = e.target.result.

    -
  8. -
  9. -

    Ensuite, on vérifie si le curseur contient une entrée de l'object store (if(cursor){ ... }) — si c'est le cas, on crée des éléments du DOM, les remplit avec les données de l'entrée, et les insère dans la page (à l'intérieur de l'élément <ul>). On inclut un bouton de suppression, qui, quand il est cliqué, supprime l'entrée en cours en appelant la fonction deleteItem() — que nous allons voir dans la section suivante.

    -
  10. -
  11. -

    À la fin du bloc if, on utilise la méthode {{domxref("IDBCursor.continue()")}} pour avancer le curseur à la prochaine entrée dans l'object store et réexécuter le bloc. S'il reste une autre entrée sur laquelle itérer, elle sera à son tour insérée dans la page, continue() sera exécuté à nouveau, et ainsi de suite.

    -
  12. -
  13. -

    Quand il n'y a plus d'enregistrements à parcourir, le curseur retourne undefined, et le bloc else sera donc exécuté à la place. Ce bloc vérifie si des notes ont été insérées dans le <ul> — si ce n'est pas le cas, on insère un message indiquant qu'il n'existe aucune note.

    -
  14. -
- -

Supprimer une note

- -

Come nous avons vu ci-dessus, lorsque le bouton supprimer est cliqué, la note correspondante est supprimée. Cette action est réalisée par la fonction deleteItem(), que l'on définit ainsi :

- -
// Définit la fonction deleteItem()
-function deleteItem(e) {
-  // Récupère l'id de l'entrée que l'on veut supprimer
-  // On doit le convertir en nombre avant d'essayer de récupérer l'entrée correspondante dans IDB
-  // les clés sont sensibles à la casse
-  let noteId = Number(e.target.parentNode.getAttribute('data-note-id'));
-
-  // Ouvre une transaction et supprime la note ayant l'id récupéré ci-dessus
-  let transaction = db.transaction(['notes'], 'readwrite');
-  let objectStore = transaction.objectStore('notes');
-  let request = objectStore.delete(noteId);
-
-  // Indique à l'utilisateur que l'entrée a été supprimée
-  transaction.oncomplete = function() {
-    // supprime l'élément parent du bouton, le li
-    // pour qu'il ne soit plus affiché
-    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
-    console.log('Note ' + noteId + ' deleted.');
-
-    // Si la liste est vide, affiche un message qui l'indique
-    if(!list.firstChild) {
-      let listItem = document.createElement('li');
-      listItem.textContent = 'No notes stored.';
-      list.appendChild(listItem);
-    }
-  };
-}
- - - -

Et voilà ! L'exemple devrait maintenant fonctionner.

- -
-

Note : Si vous rencontrez des difficultés, n'hésitez pas à consulter notre exemple en direct (ou voir le code source).

-
- -

Stocker des données complexes avec IndexedDB

- -

Comme nous l'avons mentionné auparavant, IndexedDB peut être utilisé pour stocker plus que de simples chaînes de caractères. On peut stocker à peu près tout ce qu'on veux, y compris des objets complexes tels que des vidéos ou des images. Et ce n'est pas plus difficilte à réaliser qu'avec n'importe quel autre type de données.

- -

Pour vous montrer comment le faire, nous avons écrit un autre exemple appelé IndexedDB video store (le voir en direct). Lorsque vous exécutez l'exemple pour la première fois, il télécharge des vidéos à partir du réseau, les stocke dans une base de données IndexedDB, puis affiche les vidéos dans des éléments {{htmlelement("video")}} de l'interface utilisateur. Les prochaines fois que vous l'exécutez, il récupère les vidéos de la base de données — cela rend les chargements suivants beaucoup plus rapides et moins gourmands en bande passante.

- -

Passons en revue les parties les plus intéressantes de l'exemple. Nous ne regarderons pas tout — une grande partie est similaire à l'exemple précédent, et le code est bien commenté.

- -
    -
  1. -

    Pour cet exemple, nous avons stocké le nom des vidéos à récupérer dans un tableau d'objets :

    - -
    const videos = [
    -  { 'name' : 'crystal' },
    -  { 'name' : 'elf' },
    -  { 'name' : 'frog' },
    -  { 'name' : 'monster' },
    -  { 'name' : 'pig' },
    -  { 'name' : 'rabbit' }
    -];
    -
  2. -
  3. -

    Pour commencer, une fois que la base de données a été ouverte, on exécute la fonction init(). Elle boucle sur les noms des vidéos et essaie de charger l'entrée correspondante dans la base de données videos.

    - -

    On peut facilement vérifier si une entrée a été trouvée en vérifiant si request.result est évalué à true — si l'entrée n'est pas présente, la valeur retournée est undefined.

    - -

    Les vidéos présentes en base de données (stockées sous formes de blobs), sont directement passées à la fonction displayVideo() pour les afficher dans l'interface utilisateur. Pour les vidéos non présentes, on appelle la fonction fetchVideoFromNetwork(), qui récupère la vidéo à partir du réseau.

    - -
    function init() {
    -  // Boucle sur les vidéos une par une
    -  for(let i = 0; i < videos.length; i++) {
    -    // Ouvre une transaction, récupère l'object store, et récupère chaque video par son nom
    -    let objectStore = db.transaction('videos').objectStore('videos');
    -    let request = objectStore.get(videos[i].name);
    -    request.onsuccess = function() {
    -      // Si l'entrée existe dans la BDD (le résultat n'est pas undefined)
    -      if(request.result) {
    -        // Affiche la vidéo en utilisant displayVideo()
    -        console.log('taking videos from IDB');
    -        displayVideo(request.result.mp4, request.result.webm, request.result.name);
    -      } else {
    -        // Récupère la vidéo à partir du réseau
    -        fetchVideoFromNetwork(videos[i]);
    -      }
    -    };
    -  }
    -}
    -
  4. -
  5. -

    Le bout de code qui suit est extrait de la fonction fetchVideoFromNetwork() — ici, on récupère les versions MP4 et WebM de la vidéos en utilisant deux requêtes {{domxref("fetch()", "WindowOrWorkerGlobalScope.fetch()")}} distinctes. On utilise ensuite la méthode {{domxref("blob()", "Body.blob()")}} pour extraire la réponse sous forme de blob, ce qui nous donne une représentation objet de la vidéo que l'on peut stocker et afficher plus tard.

    - -

    Il reste cependant un problème — ces deux requêtes sont asynchrones et ont veut afficher/stocker la vidéo uniquement lorsque les deux promesses sont résolues. Heureusement, il existe une méthode native qui gère ce problème — {{jsxref("Promise.all()")}}. Elle prend un argument — la liste de toutes les promesses qui doivent être attendues — et retourne elle-même une promesse. Quand toutes les promesses sont résolues, alors la promesse de la méthode all() est résolue, avec pour valeur un tableau contenant toutes les valeurs individuelles retournées par les promesses.

    - -

    À l'intérieur du bloc all(), vous pouvez voir qu'on appelle la fonction displayVideo(), comme on l'a fait précédemment, pour afficher les vidéos dans l'interface utilisateur, puis la fonction storeVideo() pour stocker ces vidéos dans la base de données.

    - -
    let mp4Blob = fetch('videos/' + video.name + '.mp4').then(response =>
    -  response.blob()
    -);
    -let webmBlob = fetch('videos/' + video.name + '.webm').then(response =>
    -  response.blob()
    -);
    -
    -// Exécuter le bloc de code suivant lorsque les deux promesses sont résolues
    -Promise.all([mp4Blob, webmBlob]).then(function(values) {
    -  // Afficher la vidéo récupérée à partir du réseau avec displayVideo()
    -  displayVideo(values[0], values[1], video.name);
    -  // La stocker dans IDB avec storeVideo()
    -  storeVideo(values[0], values[1], video.name);
    -});
    -
  6. -
  7. -

    Regardons storeVideo() en premier. Cela ressemble beaucoup à ce qu'on a fait dans l'exemple précédent pour ajouter des données à la base de données — on ouvre une transaction en lecture/écriture et on récupère l'object store de videos, on crée un objet à ajouter à la base de données et on l'ajoute avec {{domxref("IDBObjectStore.add()")}}.

    - -
    function storeVideo(mp4Blob, webmBlob, name) {
    -  // Ouvre une transaction, récupère object store
    -  let objectStore = db.transaction(['videos'], 'readwrite').objectStore('videos');
    -  // Crée une entrée à ajouter à IDB
    -  let record = {
    -    mp4 : mp4Blob,
    -    webm : webmBlob,
    -    name : name
    -  }
    -
    -  // Ajoute l'entrée à IDB avec add()
    -  let request = objectStore.add(record);
    -
    -  ...
    -
    -};
    -
  8. -
  9. -

    Enfin, displayVideo() crée les éléments DOM nécessaires pour insérer la vidéo dans l'interface utilisateur, puis les ajoute à la page. Les parties les plus intéressantes sont copiées ci-dessous — pour afficher notre blob vidéo dans un élément <video>, on doit créer un objet URL (URL interne qui pointe vers un blob en mémoire) en utilisant la méthode {{domxref("URL.createObjectURL()")}}. Une fois que c'est fait, on peut assigner l'URL comme valeur d'attribut src de l'élément {{htmlelement("source")}}, et ça marche.

    - -
    function displayVideo(mp4Blob, webmBlob, title) {
    -  // Crée l'objet URL à partir du blob
    -  let mp4URL = URL.createObjectURL(mp4Blob);
    -  let webmURL = URL.createObjectURL(webmBlob);
    -
    -  ...
    -
    -  let video = document.createElement('video');
    -  video.controls = true;
    -  let source1 = document.createElement('source');
    -  source1.src = mp4URL;
    -  source1.type = 'video/mp4';
    -  let source2 = document.createElement('source');
    -  source2.src = webmURL;
    -  source2.type = 'video/webm';
    -
    -  ...
    -}
    -
  10. -
- -

Stockage hors-ligne de ressources

- -

L'exemple ci-dessus montre comment créer une application qui stocke des ressources volumineuses dans une base de données IndexedDB, évitant ainsi de devoir les télécharger plus d'une fois. C'est déjà une grande amélioration pour l'expérience utilisateur, mais il manque encore une chose: les fichiers HTML, CSS, et JavaScript doivent encore être téléchargés à chaque fois que le site est accédé, ce qui veut dire qu'il ne fonctionnera pas lorsqu'il n'y a pas de connexion réseau

- -

- -

C'est là qu'interviennet les Service workers et l'API étroitement liée, Cache.

- -

Service Worker / Cache

- -

Un service worker est un fichier JavaScript qui, pour faire simple, est associé à une origine (un site web à un domaine donné) lorsque le navigateur y accède. Une fois associé, il peut contrôler les pages disponibles pour cette origine. Il le fait en s'installant entre la page chargée et le réseau, interceptant les requêtes réseau visant cette origine.

- -

Quand le service worker intercepte une requête, il peut faire tout ce que vous voulez (voir quelques idées de cas d'utilisation), mais l'exemple le plus classique est de sauvegarder les réponses réseau hors-ligne pour fournir ces réponses aux requêtes qui suivent au lieu d'utiliser le réseau. Ainsi, cela vous permet de faire fonctionner un site web complètement hors-ligne.

- -

L'API Cache est un autre mécanisme de stockage côté client, il a été conçu pour enregistrer les réponses HTTP et fonctionne donc très bien en synergie avec les service workers.

- -
-

Note : Les Service workers et Cache sont pris en charge par la plupart des navigateurs modernes aujourd'hui. Au moment de la rédaction de cet article, Safari était encore occupé à l'implémenter, mais il devrait bientôt être disponible.

-
- -

Un exemple service worker

- -

Voyons un exemple, pour vous donner une idée de ce à quoi cela pourrait ressembler. Nous avons crée une autre version de l'exemple video store vu précédemment. Cela fonctionne de manière identique, mais enregistre également le HTML, CSS, et JavaScript dans l'API Cache via un service worker, permettant à l'exemple de marcher hors ligne!

- -

Voir IndexedDB video store avec service worker en direct, ou voir le code source.

- -

Enregistrer le service worker

- -

La première chose à noter est qu'il  a un peu plus de code placé dans le fichier JavaScript principal (voir index.js):

- -
// Enregistre un service worker pour contrôler le site hors-ligne
-if('serviceWorker' in navigator) {
-  navigator.serviceWorker
-           .register('/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js')
-           .then(function() { console.log('Service Worker Registered'); });
-}
- - - -
-

Note : Le chemin du fichier sw.js est relatif à l'origine du site, et non au fichier JavaScript qui l'appelle.
- Le service worker est sur https://mdn.github.io/learning-area/.../sw.js. L'origine est https://mdn.github.io. Le chemin donné doit donc être /learning-area/.../sw.js.
- Si vous vouliez héberger cet exemple sur votre propre serveur, vous devriez changer le chemin en conséquence. C'est plutôt inhabituel, mais cela doit fonctionner de cette façon pour des raisons de sécurité.

-
- -

Installer le service worker

- -

Quand une page sous le contrôle du service worker est appelée (par exemple lorsque l'exemple est rechargé), alors le service worker est installé par rapport à cette page et il peut commencer à la contrôler. Quand cela arrive, un événement install est déclenché sur le service worker; vous pouvez écrire du code dans le service worker pour qu'il réponde à cette installation.

- -

Prenons pour exemple le fichier sw.js (le service worker) :

- -
self.addEventListener('install', function(e) {
- e.waitUntil(
-   caches.open('video-store').then(function(cache) {
-     return cache.addAll([
-       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/',
-       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html',
-       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js',
-       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/style.css'
-     ]);
-   })
- );
-});
- -
    -
  1. -

    Le gestionnaire d'événément install est enregistré sur self. Le mot-clé self est un moyen de faire référence au service worker de la portée globale à partir de son fichier.

    -
  2. -
  3. -

    À l'intérieur du gestionnaire d'installation, on utilise la méthode {{domxref("ExtendableEvent.waitUntil()")}}, disponible sur l'objet événement, pour signaler que le navigateur ne doit pas terminer l'installation du service worker avant que la promesse qu'il contient ne soit résolue avec succès.

    -
  4. -
  5. -

    Ici, on voit l'API Cache en action: on utilise la méthode {{domxref("CacheStorage.open()")}} pour ouvrir un nouvel objet cache dans lequel les réponses seront stockées (similaire à un object store IndexedDB). Cette promesse se résout avec un objet {{domxref("Cache")}} représentant le cache du video-store.

    -
  6. -
  7. -

    On utilise la méthode {{domxref("Cache.addAll()")}} pour récupérer une série de ressources et ajouter leur réponse au cache.

    -
  8. -
- -

C'est tout pour l'instant, l'installation est terminée.

- -

Répondre aux futures requêtes

- -

Avec le service worker enregistré et installé pour notre page HTML, et les ressources pertinentes ajoutées au cache, on est presque prêts. Il n'y a plus qu'une chose à faire: écrire du code pour répondre aux prochaines requêtes réseau.

- -

C'est ce que fait le second bloc de code dans sw.js :

- -
self.addEventListener('fetch', function(e) {
-  console.log(e.request.url);
-  e.respondWith(
-    caches.match(e.request).then(function(response) {
-      return response || fetch(e.request);
-    })
-  );
-});
- -
    -
  1. -

    On ajoute un deuxième gestionnaire d'événement au service worker, qui exécute une fonction quand l'événement fetch est déclenché. Cela arrive quand le navigateur requête une ressource dans le même répertoire que le service worker (ou sous-répertoire).

    -
  2. -
  3. -

    À l'intérieur de cette fonction, on affiche l'URL de la ressource demandée dans la console, et on utilise la méthode {{domxref("FetchEvent.respondWith()")}} pour retourner une réponse personnalisée à la requête.

    -
  4. -
  5. -

    Pour construire la réponse, on utilise d'abord {{domxref("CacheStorage.match()")}} afin de vérifier si la requête est en cache (qu'une requête correspond à l'URL demandée est en cache).

    -
  6. -
  7. -

    Si elle est trouvée, la promesse se résout avec la réponse correspondante; sinon, avec undefined. Dans ce cas, on récupère la réponse à partir du réseau, en utilisant fetch(), et on retourne le résultat.

    -
  8. -
- -

C'est tout pour notre service worker. Il y a tout un tas de choses que vous pouvez faire avec — pour plus de détails, consultez le service worker cookbook. Et merci à Paul Kinlan pour son article Adding a Service Worker and Offline into your Web App, qui a inspiré cet exemple.

- -

Tester l'exemple hors-ligne

- -

Pour tester notre exemple de service worker, rechargez d'abord la page pour vous assurer qu'il est bien installé. Une fois que c'est fait, vous pouvez soit:

- - - -

Si vous actualisez votre page d'exemple, vous devriez toujours la voir se charger normalemment. Tout est stocké hors connexion — les ressources de la page dans Cache et les vidéos dans une base de données IndexedDB.

- -

Sommaire

- -

C'est tout pour l'instant. Nous espérons que vous avez trouvé notre récapitulatif des technologies de stockage côté client utile.

- -

Voir aussi

- - - -

{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.md b/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.md new file mode 100644 index 0000000000..60cc11cd4a --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/client-side_storage/index.md @@ -0,0 +1,882 @@ +--- +title: Stockage côté client +slug: Learn/JavaScript/Client-side_web_APIs/Client-side_storage +tags: + - API + - Apprendre + - Codage + - Débutant + - Guide + - IndexedDB + - JavaScript + - Storage +translation_of: Learn/JavaScript/Client-side_web_APIs/Client-side_storage +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Client-side_storage +--- +

{{LearnSidebar}}

+ +
{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
+ +
+ +

Les navigateurs web modernes permettent aux sites web de stocker des données sur l'ordinateur de l'utilisateur — avec sa permission — puis de les récupérer au besoin. Cela permet d'enregistrer des données pour du stockage à long terme, de sauvegarder des documents ou des sites hors-ligne, de conserver des préférences spécifiques à l'utilisateur et plus encore. Cet article explique les fondamentaux pour y parvenir.

+ + + + + + + + + + + + +
Prérequis:Notions de bases de JavaScript (voir premiers pas, les briques JavaScript, les objets JavaScript), les notions de base des APIs côté client
Objectif:Apprendre à utiliser les APIs de stockage côté client pour stocker des données de l'application.
+ +

Stockage côté client ?

+ +

Ailleurs dans la zone d'apprentissage de MDN, nous avons parlé de la différence entre les sites statiques et les sites dynamiques — ces derniers stockent des données côté serveur en utilisant une base de données. Ensuite, ils exécutent du code pour récupérer les données et les insérer dans des templates de page statique. Finalement, le HTML résultant est envoyé au client, qui est alors affiché par le navigateur de l'utilisateur.

+ +

Le stockage côté client fonctionne sur des principes similaires, mais pour une utilisation différente. Le stockage côté client repose sur des APIs JavaScript qui permettent de stocker des données sur la machine de l'utilisateur et de les récupérer au besoin. Cela peut se révéler utile dans différents cas comme :

+ + + +

Souvent, le stockage côté client et côté serveur sont utilisés ensemble. Par exemple, vous pouvez télécharger à partir d'une base de données côté serveur une série de fichiers mp3 utilisés par un site web (comme un jeu ou une application de musique) vers une base de données côté client et ainsi pouvoir les lire quand vous le voulez. Avec cette stratégie, l'utilisateur n'a à télécharger le fichier qu'une seule fois — les visites suivantes, ils sont récupérés à partir de la base de données locale.

+ +
+

Note : La quantité de données que l'on peut stocker à l'aide des APIs de stockage côté client est limitée (limite par API et limite globale), la limite exacte dépend du navigateur et des configurations. Voir Limites de stockage du navigateur et critères d'éviction pour plus d'informations.

+
+ +

À l'ancienne : les cookies

+ +

Le concept de stockage côté client existe depuis longtemps. Au début du web, les sites utilisaient des cookies pour stocker des informations et personnaliser l'expérience utilisateur. C'est la méthode de stockage côté client la plus couramment utilisée et la plus ancienne.

+ +

De par leur histoire, les cookies souffrent d'un certain nombre de problèmes — tant techniques qu'au niveau de l'expérience utilisateur. Ces problèmes sont suffisamment importants pour imposer un message d'information aux utilisateurs habitant en Europe lors de leur première visite si le site utilise des cookies pour stocker des informations sur eux. Cela est dû à une loi de l'Union Européenne connue sous le nom de directive Cookie.

+ +

+ +

Pour ces raisons, nous ne verrons pas dans cet article comment utiliser les cookies. Entre le fait qu'ils sont dépassés, les problèmes de sécurité qu'ils présentent et l'incapacité de stocker des données complexes, les cookies ne sont pas la meilleure manière pour stocker des données. Il y a de meilleures alternatives, modernes, permettant de stocker des données variées sur l'ordinateur de l'utilisateur.

+ +

Le seul avantage des cookies est qu'ils sont supportés par des navigateurs anciens : si votre projet requiert le support de navigateurs obsolètes (comme Internet Explorer 8 et inférieur), les cookies peuvent se révéler utiles. Pour la plupart des projets, vous ne devriez pas avoir besoin d'y recourir.

+ +
+

Note : Pourquoi existe-t-il encore de nouveaux sites crées à l'aide de cookies? Principalement de par les habitudes des développeurs, l'utilisation de bibliothèques anciennes qui utilisent encore des cookies et l'existence de nombreux sites web fournissant des formations et références dépassées pour apprendre à stocker des données.

+
+ +

La nouvelle école : Web Storage et IndexedDB

+ +

Les navigateurs modernes ont des APIs beaucoup plus efficaces et faciles d'utilisation pour stocker des données côté client.

+ + + +

Vous en apprendrez plus sur ces APIs ci-dessous.

+ +

Le futur : l'API Cache

+ +

Certains navigateurs modernes prennent en charge la nouvelle API {{domxref("Cache")}}. Cette API a été conçue pour stocker les réponses HTTP de requêtes données et est très utile pour stocker des ressources du site afin qu'il soit accessible sans connexion réseau par exemple. Le cache est généralement utilisé avec l'API Service Worker, mais ce n'est pas obligatoire.

+ +

L'utilisation du Cache et des Service Workers est un sujet avancé, nous ne le traiterons pas en détail dans cet article, nous ne montrerons qu'un simple exemple dans la section {{anch("Stockage hors-ligne de ressources")}} plus bas.

+ +

Stocker des données simples — web storage

+ +

L'API Web Storage est très facile à utiliser — on stocke une simple paire clé/valeur de données (limité aux données scalaires) et on les récupére au besoin.

+ +

Syntaxe basique

+ +

Nous allons vous guider pas à pas :

+ +
    +
  1. +

    Tout d'abord, ouvez notre template vide de web storage sur GitHub dans un nouvel onglet.

    +
  2. +
  3. +

    Ouvrez la console JavaScript de votre navigateur.

    +
  4. +
  5. +

    Toutes les données du web storage sont contenues dans deux structures de type objet : {{domxref("Window.sessionStorage", "sessionStorage")}} et {{domxref("Window.localStorage", "localStorage")}}. Le premier conserve les données aussi longtemps que le navigateur est ouvert (elles sont perdues lorsque le navigateur est fermé) et le second conserve les données même après que le navigateur ait été fermé puis ré-ouvert. Nous allons utiliser le second dans cet article car il est généralement plus utile.

    + +

    La méthode {{domxref("Storage.setItem()")}} permet de sauvegarder des données dans le storage — elle prend deux paramètres : le nom de l'entrée à enregistrer et sa valeur. Essayez de taper ce qui suit dans votre console JavaScript (changez le nom et la valeur si vous le voulez !) :

    + +
    localStorage.setItem('name','Chris');
    +
  6. +
  7. +

    La méthode {{domxref("Storage.getItem()")}} prend un paramètre — le nom de l'entrée que vous voulez récupérer — et retourne la valeur de l'entrée. Maintenant, tapez ces lignes dans votre console JavaScript :

    + +
    var myName = localStorage.getItem('name');
    +myName
    + +

    En tapant la deuxième ligne, vous devriez voir que la variable myName contient la valeur de l'entrée name.

    +
  8. +
  9. +

    La méthode {{domxref("Storage.removeItem()")}} prend un paramètre — le nom de l'entrée de vous voulez supprimer — et supprime l'entrée du web storage. Tapez les lignes suivantes dans votre console JavaScript :

    + +
    localStorage.removeItem('name');
    +var myName = localStorage.getItem('name');
    +myName
    + +

    La troisième ligne devrait maintenant retourner null — l'entrée name n'existe plus dans le web storage.

    +
  10. +
+ +

Les données persistent !

+ +

Une caractéristique clé du web storage est que les données persistent entre les différents chargements de page (et même lorsque le navigateur est arrêté dans le cas du localStorage). Regardons ça en action :

+ +
    +
  1. +

    Ouvrez notre template vide une fois de plus, mais cette fois dans un navigateur différent de celui dans lequel vous avez ouvert ce tutoriel. Cela rendra la suite plus facile.

    +
  2. +
  3. +

    Tapez ces lignes dans la console JavaScript du navigateur que vous venez d'ouvrir :

    + +
    localStorage.setItem('name','Chris');
    +var myName = localStorage.getItem('name');
    +myName
    + +

    Vous devriez voir que l'entrée name est bien là.

    +
  4. +
  5. +

    Maintenant, fermez le navigateur et ouvrez-le de nouveau.

    +
  6. +
  7. +

    Entrez les lignes suivantes :

    + +
    var myName = localStorage.getItem('name');
    +myName
    + +

    Vous devriez voir que la valeur est toujours accessible, quand bien même le navigateur a été redémarré.

    +
  8. +
+ +

Stockage séparé pour chaque domaine

+ +

Il existe un système de stockage distinct pour chaque domaine (chaque adresse web chargée dans le navigateur a accès à son propre storage et pas aux autres). Vous verrez que si vous chargez deux sites web (disons google.com et amazon.com) et essayez de stocker un élément, il ne sera pas disponible sur l'autre site.

+ +

C'est plutôt logique — imaginez les problèmes de sécurité qui se poseraient si les sites web pouvaient voir les données d'un autre !

+ +

Un exemple plus impliqué

+ +

Appliquons cette nouvelle connaissance pour écrire un exemple, cela vous donnera une idée de la façon dont le web storage peut être utilisé. Notre exemple permettra d'envoyer un nom, à la suite de quoi la page sera mise à jour pour donner un accueil personnalisé. Cet état persistera également après un rechargement de la page ou redémarrage du navigateur, puisqu'il sera stocké dans le web storage.

+ +

Le HTML de l'exemple est disponible à personal-greeting.html — il s'agit d'un site web très simple avec entête, contenu et pied de page, ainsi qu'un formulaire pour entrer votre nom.

+ +

+ +

Nous allons construire cet exemple pas à pas, cela vous permettra de comprendre comment ça marche.

+ +
    +
  1. +

    D'abord, copiez notre fichier personal-greeting.html dans un nouveau répertoire sur votre ordinateur.

    +
  2. +
  3. +

    Ensuite, créez un fichier index.js dans le même répertoire que le fichier HTML — le fichier HTML inclut ce script (voir ligne 40).

    +
  4. +
  5. +

    Nous allons commencer par récupérer les références de tous les éléments HTML qu'on manipulera dans cet exemple — nous les créons en tant que constantes car ces références n'ont pas besoin d'être modifiées au cours de l'exécution de l'application. Ajoutez les lignes suivantes à votre fichier JavaScript:

    + +
    // créer les constantes nécessaires
    +const rememberDiv = document.querySelector('.remember');
    +const forgetDiv = document.querySelector('.forget');
    +const form = document.querySelector('form');
    +const nameInput = document.querySelector('#entername');
    +const submitBtn = document.querySelector('#submitname');
    +const forgetBtn = document.querySelector('#forgetname');
    +
    +const h1 = document.querySelector('h1');
    +const personalGreeting = document.querySelector('.personal-greeting');
    +
  6. +
  7. +

    Ensuite, on doit ajouter un gestionnaire d'événement pour empêcher le formulaire d'être véritablement soumis lorsque le bouton de soumission est cliqué, puisque ce n'est pas le comportement que l'on veut. Ajoutez le bout de code suivant à la suite de du code précédent :

    + +
    // Empêcher le form d'être soumis
    +form.addEventListener('submit', function(e) {
    +  e.preventDefault();
    +});
    +
  8. +
  9. +

    Maintenant, on doit ajouter un gestionnaire d'événement pour gérer le clic sur le bouton "Say hello" (dire bonjour). Les commentaires expliquent ce que chaque instruction fait, mais, en substance, on prend le nom que l'utilisateur a entré dans le champs texte et on l'enregistre dans le web storage avec setItem(). Ensuite, on exécute une fonction appelée nameDisplayCheck() qui se charge de mettre à jour le contenu du site web. Ajoutez ceci au bas de votre code :

    + +
    // exécuter la fonction quand le bouton 'Say hello' est cliqué
    +submitBtn.addEventListener('click', function() {
    +  // stocker le nom entré dans le web storage
    +  localStorage.setItem('name', nameInput.value);
    +  // exécuter nameDisplayCheck() pour afficher la
    +  // page personnalisée et changer le formulaire
    +  nameDisplayCheck();
    +});
    +
  10. +
  11. +

    On doit maintenant gérer l'événement lorsque le bouton "Forget" (oublier) est cliqué — il est affiché une fois que le bouton "Say hello" a été cliqué (les deux boutons permettent de basculer d'un état à l'autre). Dans cette fonction, on supprime l'élément name du web storage en utilisant removeItem(), puis on exécute nameDisplayCheck() pour mettre à jour l'affichage. Ajoutez ceci au bas de votre code :

    + +
    // exécuter la fonction quand le bouton 'Forget' est cliqué
    +forgetBtn.addEventListener('click', function() {
    +  // supprimer l'item name du web storage
    +  localStorage.removeItem('name');
    + // exécuter nameDisplayCheck() pour afficher la
    + // page personnalisée et changer le formulaire
    +  nameDisplayCheck();
    +});
    +
  12. +
  13. +

    Il est maintenant temps de définir la fonction nameDisplayCheck() elle-même. Ici, on vérifie si l'élément name est stocké dans le web storage en utilisant localStorage.getItem('name') comme condition. S'il existe, la valeur retournée sera évaluée à true; sinon, comme false. S'il existe, on affiche un message d'accueil personnalisé et le bouton "Forget" du formulaire, tout en masquant le bouton "Say hello" du formulaire. Sinon, on affiche un message d'accueil générique et le bouton "Say hello". Encore une fois, mettez les lignes suivantes au bas de votre code :

    + +
    // définit la fonction nameDisplayCheck()
    +function nameDisplayCheck() {
    +  // vérifie si l'élément 'name' est stocké dans le web storage
    +  if(localStorage.getItem('name')) {
    +    // Si c'est le cas, affiche un accueil personnalisé
    +    let name = localStorage.getItem('name');
    +    h1.textContent = 'Welcome, ' + name;
    +    personalGreeting.textContent = 'Welcome to our website, ' + name + '! We hope you have fun while you are here.';
    +    // cache la partie 'remember' du formulaire et affiche la partie 'forget'
    +    forgetDiv.style.display = 'block';
    +    rememberDiv.style.display = 'none';
    +  } else {
    +    // Sinon, affiche un accueil générique
    +    h1.textContent = 'Welcome to our website ';
    +    personalGreeting.textContent = 'Welcome to our website. We hope you have fun while you are here.';
    +    // cache la partie 'forget' du formulaire et affiche la partie 'remember'
    +    forgetDiv.style.display = 'none';
    +    rememberDiv.style.display = 'block';
    +  }
    +}
    +
  14. +
  15. +

    Dernier point, mais non des moindres, on exécute la fonction nameDisplayCheck() à chaque fois que la page est chargée. Si on ne le faisait pas, l'accueil personnalisé ne serait pas affiché après qu'on ait rafraichit la page. Ajoutez ce qui suit au bas de votre code :

    + +
    document.body.onload = nameDisplayCheck;
    +
  16. +
+ +

Notre exemple est terminé — bien joué ! Il ne vous reste plus qu'à enregistrer votre code et tester votre page HTML dans un navigateur. Vous pouvez voir notre version terminée en direct ici (ou le code JavaScript terminé).

+ +
+

Note : Vous pouvez trouver un exemple un peu plus complexe dans l'article Utiliser l'API de stockage web.

+
+ +
+

Note : Dans la ligne <script src="index.js" defer></script> de notre version finie, l'attribut defer spécifie que le contenu de l'élément {{htmlelement("script")}} ne doit pas s'exécuter avant que la page ait fini de charger.

+
+ +

Stocker des données complexes — IndexedDB

+ +

L'API IndexedDB (parfois abrégé IDB) est un système de base de données complet disponible dans le navigateur. Vous pouvez y stocker des données complexes, les types ne sont pas limités à des valeurs simples de type chaînes ou nombres. Vous pouvez stocker des vidéos, des images et à peu près tout ce que vous voulez, dans une instance IndexedDB.

+ +

Cependant, cela a un coût : IndexedDB est beaucoup plus complexe à utiliser que l'API Web Storage. Dans cette section, nous ne ferons qu'égratigner la surface de ce qu'IndexedDB peut faire, mais nous vous en donnerons assez pour débuter.

+ +

Un exemple de stockage de notes

+ +

Nous allons voir un exemple qui vous permettra de stocker des notes dans votre navigateur, les voir et les supprimer, quand vous le souhaitez. Vous apprendrez à le construire par vous-même au fur et à mesure des explications et cela vous permettra de comprendre les parties fondamentales d'IDB.

+ +

L'application ressemble à ceci :

+ +

+ +

Chaque note a un titre et une description, chacun éditables individuellement. Le code JavaScript que nous allons voir ci-dessous contient des commentaires détaillés pour vous aider à comprendre ce qu'il se passe.

+ +

Pour commencer

+ +
    +
  1. Tout d'abord, copiez les fichiers index.html, style.css, et index-start.js dans un nouveau répertoire sur votre ordinateur.
  2. +
  3. Jetez un coup d'oeil aux fichiers. +
      +
    • Vous verrez que le HTML est assez simple : un site web avec une entête et un pied de page, ainsi qu'une zone de contenu principal contenant un emplacement pour afficher les notes et un formulaire pour en ajouter.
    • +
    • Le CSS fournit un style simple pour rendre plus clair ce qu'il se passe.
    • +
    • Le fichier JavaScript contient cinq constantes déclarées — des références à l'élément {{htmlelement("ul")}} dans lequel seront affichées les notes, les {{htmlelement("input")}} title et body, le {{htmlelement("form")}} lui-même, et un {{htmlelement("button")}}.
    • +
    +
  4. +
  5. Renommez votre fichier JavaScript en index.js. Vous êtes maintenant prêt pour y ajouter du code.
  6. +
+ +

Configuration initiale de la base de données

+ +

Voyons maintenant la première chose à faire, mettre en place la base de données.

+ +
    +
  1. +

    À la suite des déclarations de constantes, ajoutez les lignes suivantes :

    + +
    // Objet db pour stocker la BDD ouverte
    +let db;
    + +

    Ici, on déclare une variable appelée db — on l'utilisera plus tard pour stocker un objet permettant d'accéder à la base de données. On l'utilisera à plusieurs endroits, on l'a donc déclaré globablement ici pour faciliter les choses.

    +
  2. +
  3. +

    Ensuite, ajoutez ce qui suit au bas de votre code :

    + +
    window.onload = function() {
    +
    +};
    + +

    On écrira tout notre code dans le gestionnaire d'événement window.onload, appelé quand l'événement {{event("load")}} de la fenêtre est chargé, pour s'assurer qu'on n'essaiera pas d'utiliser IndexedDB avant que l'application ne soit complètement chargée (ça ne marcherait pas sinon).

    +
  4. +
  5. +

    À l'intérieur de window.onload, ajoutez ce qui suit :

    + +
    // Ouvrir la BDD; elle sera créée si elle n'existe pas déjà
    +// (voir onupgradeneeded)
    +let request = window.indexedDB.open('notes', 1);
    + +

    Cette ligne crée une requête request pour ouvrir la version 1 de la base de données appelée notes. Si elle n'existe pas déjà, on devra la créer via un gestionnaire d'événement.

    + +

    Vous verrez très souvent ce format dans IndexedDB. Les opérations de base de données prennent du temps et on ne veut pas suspendre le navigateur le temps de récupérer le résultat, les opérations sur la base de données sont donc {{Glossary("asynchronous", "asynchrones")}} — ce qui signifie qu'au lieu d'arriver immédiatement, elles se produiront à un moment ultérieur et un événement sera déclenché lorsque cela arrivera.

    + +

    Pour gérer cela dans IndexedDB, on crée d'abord une requête (que vous pouvez appeler comme vous le voulez — on l'appelle request pour que ce soit plus explicite). On utilise ensuite des gestionnaire d'événement pour exécuter du code lorsque les requêtes sont terminées, échouent, etc, ce que l'on va voir ci-dessous.

    + +
    +

    Note : Le numéro de version est important. Si vous voulez mettre à jour votre base de données (par exemple, pour modifier la structure de la table), vous devez ré-exécuter votre code avec un numéro de version supérieur et spécifier le schéma de la base de données avec le gestionnaire d'événement onupgradeneeded. Nous ne verrons pas la mise à jour de base de données dans ce tutoriel.

    +
    +
  6. +
  7. +

    Maintenant, ajoutez les gestionnaires d'événement suivants, juste en dessous des lignes précédentes — toujours à l'intérieur de window.onload :

    + +
    // la base de données n'a pas pu être ouverte avec succès
    +request.onerror = function() {
    +  console.log('Database failed to open');
    +};
    +
    +// la base de données a été ouverte avec succès
    +request.onsuccess = function() {
    +  console.log('Database opened successfully');
    +
    +  // Stocke la base de données ouverte dans la variable db. On l'utilise par la suite
    +  db = request.result;
    +
    +  // Exécute la fonction displayData() pour afficher les notes qui sont dans la BDD
    +  displayData();
    +};
    + +

    Le gestionnaire d'événement {{domxref("IDBRequest.onerror", "request.onerror")}} s'exécutera si la requête échoue. Cela vous permet de gérer le problème si cela arrive. Dans notre exemple, on affiche simplement un message dans la console JavaScript.

    + +

    Le gestionnare d'événement {{domxref("IDBRequest.onsuccess", "request.onsuccess")}}, d'autre part, s'exécutera si la requête aboutit, que la base de données a été ouverte avec succès. Lorsque cela arrive, la propriété {{domxref("IDBRequest.result", "request.result")}} contient alors un objet représentant la base de données ouverte, qui nous permet de la manipuler. On stocke cette valeur dans la variable db qu'on a crée plus tôt pour pouvoir l'utiliser ensuite. On exécute également une fonction appelée displayData(), qu'on définira plus tard — elle affiche les données de la base de données dans le {{HTMLElement("ul")}}. On l'exécute dès à présent pour que les notes en base de données soient affichées dès que la page est chargée.

    +
  8. +
  9. +

    Pour en finir avec cette section, on ajoute le gestionnaire d'événement qui est  probablement le plus important, {{domxref("IDBOpenDBRequest.onupgradeneeded", "request.onupdateneeded")}}. Il est exécuté si la base de données n'a pas déjà été créée ou si on veut ouvrir la base de données avec un numéro de version supérieur à celle qui existe (pour faire une mise à jour). Ajoutez le code suivant en dessous de votre gestionnaire précédent :

    + +
    // Spécifie les tables de la BDD si ce n'est pas déjà pas fait
    +request.onupgradeneeded = function(e) {
    +  // Récupère une référence à la BDD ouverte
    +  let db = e.target.result;
    +
    +  // Crée un objectStore pour stocker nos notes (une table)
    +  // Avec un champ qui s'auto-incrémente comme clé
    +  let objectStore = db.createObjectStore('notes', { keyPath: 'id', autoIncrement:true });
    +
    +  // Définit les champs que l'objectStore contient
    +  objectStore.createIndex('title', 'title', { unique: false });
    +  objectStore.createIndex('body', 'body', { unique: false });
    +
    +  console.log('Database setup complete');
    +};
    + +

    C'est ici qu'on définit le schéma (la structure) de notre base de données; c'est à dire l'ensemble des champs (ou colonnes) qu'il contient.

    + +
      +
    1. +

      On récupère une référence à la base de données existante depuis e.target.result (la propriété result de la cible de l'événement, c'est à dire l'objet request). C'est l'équivalent de la ligne db = request.result; du gestionnaire d'événement onsuccess, mais on doit le faire de cette manière ici puisque le gestionnaire d'événement onupgradeneeded est exécuté avant onsuccess — la valeur de db n'est pas encore disponible.

      +
    2. +
    3. +

      Ensuite, on utilise {{domxref("IDBDatabase.createObjectStore()")}} pour créer un object store (un container pour une collection d'objets) à l'intérieur de notre base de données. C'est l'équivalent d'une table dans un système de base de données traditionnel. On lui a donné le nom notes, et un champs id avec autoIncrement — pour chaque nouvelle entrée dans cette table, une valeur auto-incrementée sera attributée au champ id sans que le développeur n'ait à le définir. Le champ id est la clé de l'object store: il sera utilisé pour identifier de manière unique les entrées, permettant de les mettre à jour ou les supprimer.

      +
    4. +
    5. +

      On crée deux autres index (champs) en utilisant la méthode {{domxref("IDBObjectStore.createIndex()")}}: title (qui contiendra le titre de chaque note), et body (qui contiendra la description de chaque note).

      +
    6. +
    +
  10. +
+ +

Avec ce simple schéma de base de données en place, on va pouvoir ajouter des entrées à la base de données, des objets qui ressembleront à ça :

+ +
{
+  title: "Acheter du lait",
+  body: "Lait de vache et de soja.",
+  id: 8
+}
+ +

Ajouter des données à la base de données

+ +

Maintenant, voyons comment ajouter des entrées dans la base de données. On le fera en utilisant le formulaire de notre page.

+ +
    +
  1. +

    À la suite du gestionnaire d'événement précédent (mais toujours dans window.onload), ajoutez la ligne suivante — elle définit un gestionnaire d'événement onsubmit pour exécuter la fonction addData() quand le formulaire est soumis (que le {{htmlelement("button")}} envoyer est pressé et que les champs du formulaire sont valides) :

    + +
    // Créer un gestionnaire onsubmit pour appeler la fonction addData() quand le formulaire est soumis
    +form.onsubmit = addData;
    +
  2. +
  3. +

    Maintenant, définissons la fonction addData(). Ajoutez ce qui suit après la ligne précédente :

    + +
    // Définit la fonction addData()
    +function addData(e) {
    +  // empêcher le formulaire d'être soumis vers le serveur
    +  e.preventDefault();
    +
    +  // récupérer les valeurs entrées dans les champs du formulaire
    +  // et les stocker dans un objet qui sera inséré en BDD
    +  let newItem = { title: titleInput.value, body: bodyInput.value };
    +
    +  // ouvrir une transaction en lecture/écriture
    +  let transaction = db.transaction(['notes'], 'readwrite');
    +
    +  // récupérer l'object store de la base de données qui a été ouvert avec la transaction
    +  let objectStore = transaction.objectStore('notes');
    +
    +  // demander l'ajout de notre nouvel objet à l'object store
    +  var request = objectStore.add(newItem);
    +  request.onsuccess = function() {
    +    // vider le formulaire, pour qu'il soit prêt pour un nouvel ajout
    +    titleInput.value = '';
    +    bodyInput.value = '';
    +  };
    +
    +  // attendre la fin de la transaction, quand l'ajout a été effectué
    +  transaction.oncomplete = function() {
    +    console.log('Transaction completed: database modification finished.');
    +
    +    // mettre à jour l'affichage pour montrer le nouvel item en exécutant displayData()
    +    displayData();
    +  };
    +
    +  transaction.onerror = function() {
    +    console.log('Transaction not opened due to error');
    +  };
    +}
    + +

    C'est assez complexe, voyons ça pas à pas :

    + +
      +
    1. +

      On exécute {{domxref("Event.preventDefault()")}} sur l'objet événement pour empêcher le formulaire d'être véritablement soumis (cela provoquerait une actualisation de la page et gâcherait l'expérience utilisateur).

      +
    2. +
    3. +

      On crée un objet représentant une entrée à ajouter dans la base de données, en le remplissant avec les valeurs des champs du formulaire. Notez qu'on n'a pas besoin d'inclure explicitement une valeur id — comme nous l'avons précédemment expliqué, il est auto-rempli.

      +
    4. +
    5. +

      On ouvre une transaction en lecture/écritre (readwrite) sur l'object store notes en utilisant la méthode {{domxref("IDBDatabase.transaction()")}}. Cet object transaction va nous permettre d'accéder à l'object store, pour ajouter une nouvelle entrée par exemple.

      +
    6. +
    7. +

      On récupère l'object store de la transaction avec la méthode {{domxref("IDBTransaction.objectStore()")}} et on le stocke dans la variable objectStore.

      +
    8. +
    9. +

      On ajoute un nouvel enregistrement à la base de données en utilisant {{domxref("IDBObjectStore.add()")}}. Cela crée une requête, sur le même principe qu'on a déjà vu.

      +
    10. +
    11. On ajoute des gestionnaires d'événement à request et transaction pour exécuter du code aux points importants de leur cycle de vie : +
        +
      • Quand la requête a réussit, on efface les champs du formulaire — pour pouvoir ajouter une nouvelle note
      • +
      • Quand la transaction est terminé, on réexécute la fonction displayData() — pour mettre à jour l'affichage de notes sur la page.
      • +
      +
    12. +
    +
  4. +
+ +

Afficher les données

+ +

Nous avons déjà appelé displayData() deux fois dans notre code, nous allons maintenant définir cette fonction. Ajoutez ce qui suit à votre code, en dessous de la définition de la fonction précédente :

+ +
// Définit la fonction displayData()
+function displayData() {
+  // Vide le contenu de la liste à chaque fois qu'on la met à jour
+  // Si on ne le faisait pas, des duplicats seraient affichés à chaque ajout
+  while (list.firstChild) {
+    list.removeChild(list.firstChild);
+  }
+
+  // Ouvre l'object store puis récupère un curseur - qui va nous permettre d'itérer
+  // sur les entrées de l'object store
+  let objectStore = db.transaction('notes').objectStore('notes');
+  objectStore.openCursor().onsuccess = function(e) {
+    // Récupère une référence au curseur
+    let cursor = e.target.result;
+
+    // S'il reste des entrées sur lesquelles itérer, on exécute ce code
+    if(cursor) {
+      // Crée un li, h3, et p pour mettre les données de l'entrée puis les ajouter à la liste
+      let listItem = document.createElement('li');
+      let h3 = document.createElement('h3');
+      let para = document.createElement('p');
+
+      listItem.appendChild(h3);
+      listItem.appendChild(para);
+      list.appendChild(listItem);
+
+      // Récupère les données à partir du curseur et les met dans le h3 et p
+      h3.textContent = cursor.value.title;
+      para.textContent = cursor.value.body;
+
+      // Met l'ID de l'entrée dans un attribut du li, pour savoir à quelle entrée il correspond
+      // Ce sera utile plus tard pour pouvoir supprimer des entrées
+      listItem.setAttribute('data-note-id', cursor.value.id);
+
+      // Crée un bouton et le place dans le li
+      let deleteBtn = document.createElement('button');
+      listItem.appendChild(deleteBtn);
+      deleteBtn.textContent = 'Delete';
+
+      // Définit un gestionnaire d'événement pour appeler deleteItem() quand le bouton supprimer est cliqué
+      deleteBtn.onclick = deleteItem;
+
+      // Continue l'itération vers la prochaine entrée du curseur
+      cursor.continue();
+    } else {
+      // Si la liste est vide, affiche un message "Aucune note n'existe"
+      if(!list.firstChild) {
+        let listItem = document.createElement('li');
+        listItem.textContent = 'No notes stored.';
+        list.appendChild(listItem);
+      }
+      // Il n'y a plus d'entrées dans le curseur
+      console.log('Notes all displayed');
+    }
+  };
+}
+ +

Encore une fois, pas à pas :

+ +
    +
  1. +

    D'abord on vide le contenu de l'élément {{htmlelement("ul")}}, pour pouvoir le remplir avec le contenu mis à jour. Si on ne le faisait pas, on obtiendrait une énorme liste de contenus dupliqués à chaque mise à jour.

    +
  2. +
  3. +

    Ensuite, on récupère une référence à l'object store notes en utilisant {{domxref("IDBDatabase.transaction()")}} et {{domxref("IDBTransaction.objectStore()")}} comme nous l'avons fait dans addData(), mais en chaînant ces deux instructions en une seule ligne.

    +
  4. +
  5. +

    L'étape suivante consiste à utiliser la méthode {{domxref("IDBObjectStore.openCursor()")}} pour ouvrir un curseur — une construction qui peut être utilisée pour itérer sur les entrées d'un object store. On chaîne un gestionnaire d'événement onsuccess à la fin de cette opération pour rendre le code plus concis — dès que le curseur est récupéré, le gestionnaire est exécuté.

    +
  6. +
  7. +

    On récupère une référence au curseur lui-même (un objet {{domxref("IDBCursor")}}) avec cursor = e.target.result.

    +
  8. +
  9. +

    Ensuite, on vérifie si le curseur contient une entrée de l'object store (if(cursor){ ... }) — si c'est le cas, on crée des éléments du DOM, les remplit avec les données de l'entrée, et les insère dans la page (à l'intérieur de l'élément <ul>). On inclut un bouton de suppression, qui, quand il est cliqué, supprime l'entrée en cours en appelant la fonction deleteItem() — que nous allons voir dans la section suivante.

    +
  10. +
  11. +

    À la fin du bloc if, on utilise la méthode {{domxref("IDBCursor.continue()")}} pour avancer le curseur à la prochaine entrée dans l'object store et réexécuter le bloc. S'il reste une autre entrée sur laquelle itérer, elle sera à son tour insérée dans la page, continue() sera exécuté à nouveau, et ainsi de suite.

    +
  12. +
  13. +

    Quand il n'y a plus d'enregistrements à parcourir, le curseur retourne undefined, et le bloc else sera donc exécuté à la place. Ce bloc vérifie si des notes ont été insérées dans le <ul> — si ce n'est pas le cas, on insère un message indiquant qu'il n'existe aucune note.

    +
  14. +
+ +

Supprimer une note

+ +

Come nous avons vu ci-dessus, lorsque le bouton supprimer est cliqué, la note correspondante est supprimée. Cette action est réalisée par la fonction deleteItem(), que l'on définit ainsi :

+ +
// Définit la fonction deleteItem()
+function deleteItem(e) {
+  // Récupère l'id de l'entrée que l'on veut supprimer
+  // On doit le convertir en nombre avant d'essayer de récupérer l'entrée correspondante dans IDB
+  // les clés sont sensibles à la casse
+  let noteId = Number(e.target.parentNode.getAttribute('data-note-id'));
+
+  // Ouvre une transaction et supprime la note ayant l'id récupéré ci-dessus
+  let transaction = db.transaction(['notes'], 'readwrite');
+  let objectStore = transaction.objectStore('notes');
+  let request = objectStore.delete(noteId);
+
+  // Indique à l'utilisateur que l'entrée a été supprimée
+  transaction.oncomplete = function() {
+    // supprime l'élément parent du bouton, le li
+    // pour qu'il ne soit plus affiché
+    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
+    console.log('Note ' + noteId + ' deleted.');
+
+    // Si la liste est vide, affiche un message qui l'indique
+    if(!list.firstChild) {
+      let listItem = document.createElement('li');
+      listItem.textContent = 'No notes stored.';
+      list.appendChild(listItem);
+    }
+  };
+}
+ + + +

Et voilà ! L'exemple devrait maintenant fonctionner.

+ +
+

Note : Si vous rencontrez des difficultés, n'hésitez pas à consulter notre exemple en direct (ou voir le code source).

+
+ +

Stocker des données complexes avec IndexedDB

+ +

Comme nous l'avons mentionné auparavant, IndexedDB peut être utilisé pour stocker plus que de simples chaînes de caractères. On peut stocker à peu près tout ce qu'on veux, y compris des objets complexes tels que des vidéos ou des images. Et ce n'est pas plus difficilte à réaliser qu'avec n'importe quel autre type de données.

+ +

Pour vous montrer comment le faire, nous avons écrit un autre exemple appelé IndexedDB video store (le voir en direct). Lorsque vous exécutez l'exemple pour la première fois, il télécharge des vidéos à partir du réseau, les stocke dans une base de données IndexedDB, puis affiche les vidéos dans des éléments {{htmlelement("video")}} de l'interface utilisateur. Les prochaines fois que vous l'exécutez, il récupère les vidéos de la base de données — cela rend les chargements suivants beaucoup plus rapides et moins gourmands en bande passante.

+ +

Passons en revue les parties les plus intéressantes de l'exemple. Nous ne regarderons pas tout — une grande partie est similaire à l'exemple précédent, et le code est bien commenté.

+ +
    +
  1. +

    Pour cet exemple, nous avons stocké le nom des vidéos à récupérer dans un tableau d'objets :

    + +
    const videos = [
    +  { 'name' : 'crystal' },
    +  { 'name' : 'elf' },
    +  { 'name' : 'frog' },
    +  { 'name' : 'monster' },
    +  { 'name' : 'pig' },
    +  { 'name' : 'rabbit' }
    +];
    +
  2. +
  3. +

    Pour commencer, une fois que la base de données a été ouverte, on exécute la fonction init(). Elle boucle sur les noms des vidéos et essaie de charger l'entrée correspondante dans la base de données videos.

    + +

    On peut facilement vérifier si une entrée a été trouvée en vérifiant si request.result est évalué à true — si l'entrée n'est pas présente, la valeur retournée est undefined.

    + +

    Les vidéos présentes en base de données (stockées sous formes de blobs), sont directement passées à la fonction displayVideo() pour les afficher dans l'interface utilisateur. Pour les vidéos non présentes, on appelle la fonction fetchVideoFromNetwork(), qui récupère la vidéo à partir du réseau.

    + +
    function init() {
    +  // Boucle sur les vidéos une par une
    +  for(let i = 0; i < videos.length; i++) {
    +    // Ouvre une transaction, récupère l'object store, et récupère chaque video par son nom
    +    let objectStore = db.transaction('videos').objectStore('videos');
    +    let request = objectStore.get(videos[i].name);
    +    request.onsuccess = function() {
    +      // Si l'entrée existe dans la BDD (le résultat n'est pas undefined)
    +      if(request.result) {
    +        // Affiche la vidéo en utilisant displayVideo()
    +        console.log('taking videos from IDB');
    +        displayVideo(request.result.mp4, request.result.webm, request.result.name);
    +      } else {
    +        // Récupère la vidéo à partir du réseau
    +        fetchVideoFromNetwork(videos[i]);
    +      }
    +    };
    +  }
    +}
    +
  4. +
  5. +

    Le bout de code qui suit est extrait de la fonction fetchVideoFromNetwork() — ici, on récupère les versions MP4 et WebM de la vidéos en utilisant deux requêtes {{domxref("fetch()", "WindowOrWorkerGlobalScope.fetch()")}} distinctes. On utilise ensuite la méthode {{domxref("blob()", "Body.blob()")}} pour extraire la réponse sous forme de blob, ce qui nous donne une représentation objet de la vidéo que l'on peut stocker et afficher plus tard.

    + +

    Il reste cependant un problème — ces deux requêtes sont asynchrones et ont veut afficher/stocker la vidéo uniquement lorsque les deux promesses sont résolues. Heureusement, il existe une méthode native qui gère ce problème — {{jsxref("Promise.all()")}}. Elle prend un argument — la liste de toutes les promesses qui doivent être attendues — et retourne elle-même une promesse. Quand toutes les promesses sont résolues, alors la promesse de la méthode all() est résolue, avec pour valeur un tableau contenant toutes les valeurs individuelles retournées par les promesses.

    + +

    À l'intérieur du bloc all(), vous pouvez voir qu'on appelle la fonction displayVideo(), comme on l'a fait précédemment, pour afficher les vidéos dans l'interface utilisateur, puis la fonction storeVideo() pour stocker ces vidéos dans la base de données.

    + +
    let mp4Blob = fetch('videos/' + video.name + '.mp4').then(response =>
    +  response.blob()
    +);
    +let webmBlob = fetch('videos/' + video.name + '.webm').then(response =>
    +  response.blob()
    +);
    +
    +// Exécuter le bloc de code suivant lorsque les deux promesses sont résolues
    +Promise.all([mp4Blob, webmBlob]).then(function(values) {
    +  // Afficher la vidéo récupérée à partir du réseau avec displayVideo()
    +  displayVideo(values[0], values[1], video.name);
    +  // La stocker dans IDB avec storeVideo()
    +  storeVideo(values[0], values[1], video.name);
    +});
    +
  6. +
  7. +

    Regardons storeVideo() en premier. Cela ressemble beaucoup à ce qu'on a fait dans l'exemple précédent pour ajouter des données à la base de données — on ouvre une transaction en lecture/écriture et on récupère l'object store de videos, on crée un objet à ajouter à la base de données et on l'ajoute avec {{domxref("IDBObjectStore.add()")}}.

    + +
    function storeVideo(mp4Blob, webmBlob, name) {
    +  // Ouvre une transaction, récupère object store
    +  let objectStore = db.transaction(['videos'], 'readwrite').objectStore('videos');
    +  // Crée une entrée à ajouter à IDB
    +  let record = {
    +    mp4 : mp4Blob,
    +    webm : webmBlob,
    +    name : name
    +  }
    +
    +  // Ajoute l'entrée à IDB avec add()
    +  let request = objectStore.add(record);
    +
    +  ...
    +
    +};
    +
  8. +
  9. +

    Enfin, displayVideo() crée les éléments DOM nécessaires pour insérer la vidéo dans l'interface utilisateur, puis les ajoute à la page. Les parties les plus intéressantes sont copiées ci-dessous — pour afficher notre blob vidéo dans un élément <video>, on doit créer un objet URL (URL interne qui pointe vers un blob en mémoire) en utilisant la méthode {{domxref("URL.createObjectURL()")}}. Une fois que c'est fait, on peut assigner l'URL comme valeur d'attribut src de l'élément {{htmlelement("source")}}, et ça marche.

    + +
    function displayVideo(mp4Blob, webmBlob, title) {
    +  // Crée l'objet URL à partir du blob
    +  let mp4URL = URL.createObjectURL(mp4Blob);
    +  let webmURL = URL.createObjectURL(webmBlob);
    +
    +  ...
    +
    +  let video = document.createElement('video');
    +  video.controls = true;
    +  let source1 = document.createElement('source');
    +  source1.src = mp4URL;
    +  source1.type = 'video/mp4';
    +  let source2 = document.createElement('source');
    +  source2.src = webmURL;
    +  source2.type = 'video/webm';
    +
    +  ...
    +}
    +
  10. +
+ +

Stockage hors-ligne de ressources

+ +

L'exemple ci-dessus montre comment créer une application qui stocke des ressources volumineuses dans une base de données IndexedDB, évitant ainsi de devoir les télécharger plus d'une fois. C'est déjà une grande amélioration pour l'expérience utilisateur, mais il manque encore une chose: les fichiers HTML, CSS, et JavaScript doivent encore être téléchargés à chaque fois que le site est accédé, ce qui veut dire qu'il ne fonctionnera pas lorsqu'il n'y a pas de connexion réseau

+ +

+ +

C'est là qu'interviennet les Service workers et l'API étroitement liée, Cache.

+ +

Service Worker / Cache

+ +

Un service worker est un fichier JavaScript qui, pour faire simple, est associé à une origine (un site web à un domaine donné) lorsque le navigateur y accède. Une fois associé, il peut contrôler les pages disponibles pour cette origine. Il le fait en s'installant entre la page chargée et le réseau, interceptant les requêtes réseau visant cette origine.

+ +

Quand le service worker intercepte une requête, il peut faire tout ce que vous voulez (voir quelques idées de cas d'utilisation), mais l'exemple le plus classique est de sauvegarder les réponses réseau hors-ligne pour fournir ces réponses aux requêtes qui suivent au lieu d'utiliser le réseau. Ainsi, cela vous permet de faire fonctionner un site web complètement hors-ligne.

+ +

L'API Cache est un autre mécanisme de stockage côté client, il a été conçu pour enregistrer les réponses HTTP et fonctionne donc très bien en synergie avec les service workers.

+ +
+

Note : Les Service workers et Cache sont pris en charge par la plupart des navigateurs modernes aujourd'hui. Au moment de la rédaction de cet article, Safari était encore occupé à l'implémenter, mais il devrait bientôt être disponible.

+
+ +

Un exemple service worker

+ +

Voyons un exemple, pour vous donner une idée de ce à quoi cela pourrait ressembler. Nous avons crée une autre version de l'exemple video store vu précédemment. Cela fonctionne de manière identique, mais enregistre également le HTML, CSS, et JavaScript dans l'API Cache via un service worker, permettant à l'exemple de marcher hors ligne!

+ +

Voir IndexedDB video store avec service worker en direct, ou voir le code source.

+ +

Enregistrer le service worker

+ +

La première chose à noter est qu'il  a un peu plus de code placé dans le fichier JavaScript principal (voir index.js):

+ +
// Enregistre un service worker pour contrôler le site hors-ligne
+if('serviceWorker' in navigator) {
+  navigator.serviceWorker
+           .register('/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js')
+           .then(function() { console.log('Service Worker Registered'); });
+}
+ + + +
+

Note : Le chemin du fichier sw.js est relatif à l'origine du site, et non au fichier JavaScript qui l'appelle.
+ Le service worker est sur https://mdn.github.io/learning-area/.../sw.js. L'origine est https://mdn.github.io. Le chemin donné doit donc être /learning-area/.../sw.js.
+ Si vous vouliez héberger cet exemple sur votre propre serveur, vous devriez changer le chemin en conséquence. C'est plutôt inhabituel, mais cela doit fonctionner de cette façon pour des raisons de sécurité.

+
+ +

Installer le service worker

+ +

Quand une page sous le contrôle du service worker est appelée (par exemple lorsque l'exemple est rechargé), alors le service worker est installé par rapport à cette page et il peut commencer à la contrôler. Quand cela arrive, un événement install est déclenché sur le service worker; vous pouvez écrire du code dans le service worker pour qu'il réponde à cette installation.

+ +

Prenons pour exemple le fichier sw.js (le service worker) :

+ +
self.addEventListener('install', function(e) {
+ e.waitUntil(
+   caches.open('video-store').then(function(cache) {
+     return cache.addAll([
+       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/',
+       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html',
+       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js',
+       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/style.css'
+     ]);
+   })
+ );
+});
+ +
    +
  1. +

    Le gestionnaire d'événément install est enregistré sur self. Le mot-clé self est un moyen de faire référence au service worker de la portée globale à partir de son fichier.

    +
  2. +
  3. +

    À l'intérieur du gestionnaire d'installation, on utilise la méthode {{domxref("ExtendableEvent.waitUntil()")}}, disponible sur l'objet événement, pour signaler que le navigateur ne doit pas terminer l'installation du service worker avant que la promesse qu'il contient ne soit résolue avec succès.

    +
  4. +
  5. +

    Ici, on voit l'API Cache en action: on utilise la méthode {{domxref("CacheStorage.open()")}} pour ouvrir un nouvel objet cache dans lequel les réponses seront stockées (similaire à un object store IndexedDB). Cette promesse se résout avec un objet {{domxref("Cache")}} représentant le cache du video-store.

    +
  6. +
  7. +

    On utilise la méthode {{domxref("Cache.addAll()")}} pour récupérer une série de ressources et ajouter leur réponse au cache.

    +
  8. +
+ +

C'est tout pour l'instant, l'installation est terminée.

+ +

Répondre aux futures requêtes

+ +

Avec le service worker enregistré et installé pour notre page HTML, et les ressources pertinentes ajoutées au cache, on est presque prêts. Il n'y a plus qu'une chose à faire: écrire du code pour répondre aux prochaines requêtes réseau.

+ +

C'est ce que fait le second bloc de code dans sw.js :

+ +
self.addEventListener('fetch', function(e) {
+  console.log(e.request.url);
+  e.respondWith(
+    caches.match(e.request).then(function(response) {
+      return response || fetch(e.request);
+    })
+  );
+});
+ +
    +
  1. +

    On ajoute un deuxième gestionnaire d'événement au service worker, qui exécute une fonction quand l'événement fetch est déclenché. Cela arrive quand le navigateur requête une ressource dans le même répertoire que le service worker (ou sous-répertoire).

    +
  2. +
  3. +

    À l'intérieur de cette fonction, on affiche l'URL de la ressource demandée dans la console, et on utilise la méthode {{domxref("FetchEvent.respondWith()")}} pour retourner une réponse personnalisée à la requête.

    +
  4. +
  5. +

    Pour construire la réponse, on utilise d'abord {{domxref("CacheStorage.match()")}} afin de vérifier si la requête est en cache (qu'une requête correspond à l'URL demandée est en cache).

    +
  6. +
  7. +

    Si elle est trouvée, la promesse se résout avec la réponse correspondante; sinon, avec undefined. Dans ce cas, on récupère la réponse à partir du réseau, en utilisant fetch(), et on retourne le résultat.

    +
  8. +
+ +

C'est tout pour notre service worker. Il y a tout un tas de choses que vous pouvez faire avec — pour plus de détails, consultez le service worker cookbook. Et merci à Paul Kinlan pour son article Adding a Service Worker and Offline into your Web App, qui a inspiré cet exemple.

+ +

Tester l'exemple hors-ligne

+ +

Pour tester notre exemple de service worker, rechargez d'abord la page pour vous assurer qu'il est bien installé. Une fois que c'est fait, vous pouvez soit:

+ + + +

Si vous actualisez votre page d'exemple, vous devriez toujours la voir se charger normalemment. Tout est stocké hors connexion — les ressources de la page dans Cache et les vidéos dans une base de données IndexedDB.

+ +

Sommaire

+ +

C'est tout pour l'instant. Nous espérons que vous avez trouvé notre récapitulatif des technologies de stockage côté client utile.

+ +

Voir aussi

+ + + +

{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.html b/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.html deleted file mode 100644 index 95c9f7f8e4..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.html +++ /dev/null @@ -1,923 +0,0 @@ ---- -title: Dessiner des éléments graphiques -slug: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics -tags: - - API - - Apprendre - - Articles - - Canvas - - Codage - - Débutant - - Graphismes - - JavaScript - - WebGL -translation_of: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Drawing_graphics ---- -
{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
- -

Le navigateur contient des outils de programmation graphique très puissants, du langage SVG (Scalable Vector Graphics), aux APIs pour dessiner sur les éléments HTML {{htmlelement("canvas")}}, (voir API Canvas et WebGL). Cet article fournit une introduction à canvas et introduit d'autres ressources pour vous permettre d'en apprendre plus.

- - - - - - - - - - - - -
Prérequis:Bases de JavaScript (voir premiers pas, les briques JavaScript, introduction aux objets), les notions de bases des APIs côté client
Objectif:Apprendre les bases pour dessiner sur des éléments <canvas> en utilisant JavaScript.
- -

Éléments graphiques sur le Web

- -

Comme nous en avons parlé dans notre module HTML Multimédia et Intégration, le web était à l'origine uniquement du texte, ce qui était très ennuyeux. Les images ont donc été introduites — d'abord via l'élément {{htmlelement("img")}} et plus tard via les propriétés CSS comme {{cssxref("background-image")}}, et SVG.

- -

Ce n'était cependant toujours pas assez. Tandis qu'il était possible d'utiliser CSS et JavaScript pour animer (ou manipuler) les images vectorielles SVG — puisqu'elles sont définies par le balisage — il n'y avait aucun moyen de faire de même pour les images bitmap, et les outils disponibles étaient plutôt limités. Le Web n'avait toujours pas de moyen efficace de créer des animations de jeux, des scènes 3D, et autres dispositions couramment traitées par les langages de bas niveau tels que C++ ou Java.

- -

La situation a commencé à s'améliorer quand les navigateurs ont commencé à prendre en charge l'élément {{htmlelement("canvas")}} et l' API Canvas associée — Apple l'a inventée vers 2004, et les autres navigateurs l'ont l'implémentée dans les années qui ont suivi. Comme vous le verrez dans cet article, canvas fournit de nombreux outils utiles à la création d'animation 2D, jeux, visualisations de données, et autres types d'application, particulièrement quand il est combiné à d'autres APIs que la plateforme web fournit.

- -

L'exemple ci-dessous montre une simple animation de balles qui rebondissent en canvas 2D, que nous avons déjà vue dans notre module La construction d'objet en pratique:

- -

{{EmbedGHLiveSample("learning-area/javascript/oojs/bouncing-balls/index-finished.html", '100%', 500)}}

- -

Autour de 2006-2007, Mozilla a commencé à travailler sur une implémentation expérimentale de canvas 3D. C'est devenu WebGL, lequel a gagné en popularité parmi les fournisseurs de navigateur, et a été standardisé autour de 2009-2010. WebGL permet de créer de véritables graphiques 3D dans le navigateur web; l'exemple ci-dessous montre un simple cube WebGL qui tourne:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}

- -

Cet article se concentrera principalement sur les canvas 2D, car le code WebGL brut est très complexe. Nous montrerons cependant comment utiliser une bibliothèque WebGL pour créer une scène 3D plus facilement, et vous pourrez  par la suite suivre le tutoriel WebGL, qui couvre le code WebGL brut.

- -
-

Note : Canvas est très bien pris en charge parmi les différents navigateurs, à l'exception de IE 8 (et inférieur) pour les canvas 2D, et IE 11 (et inférieur) pour WebGL.

-
- -

Apprentissage actif: Débuter avec un <canvas>

- -

Si vous voulez créer une scène 2D ou 3D sur une page web, vous devez commencer avec un élément HTML {{htmlelement("canvas")}}. Cet élément est utilisé pour définir la zone de la page où l'image sera dessinée. C'est aussi simple que d'inclure l'élément dans la page:

- -
<canvas width="320" height="240"></canvas>
- -

Cela va créer un canvas sur la page d'une taille de 320 pixels par 240.

- -

À l'intérieur des balises du canvas, vous pouvez mettre du contenu alternatif, qui est affiché si le navigateur de l'utilisateur ne prend pas en charge les canvas.

- -
<canvas width="320" height="240">
-  <p>Votre navigateur ne prend pas en charge canvas. Boo hoo!</p>
-</canvas>
- -

Bien sûr, le message ci-dessus est vraiment inutile! Dans un exemple réel, vous voudriez plutôt associer le contenu alternatif au contenu du canvas. Par exemple, si vous voulez afficher un graphique en temps réel des cours boursiers, le contenu alternatif pourrait être une image statique du dernier graphique, avec un texte indiquant quels sont les prix.

- -

Crée et dimensionner notre canvas

- -

Commençons par créer notre propre canvas, que nous utiliserons pour dessiner nos futures expériences.

- -
    -
  1. -

    Premièrement, copiez localement notre fichier 0_canvas_start.html, et ouvez-le dans votre éditeur de texte.

    -
  2. -
  3. -

    Ajoutez le code suivant à l'intérieur, juste après la balise {{htmlelement("body")}} ouvrante:

    - -
    <canvas class="myCanvas">
    -  <p>Ajouter un contenu alternatif approprié ici.</p>
    -</canvas>
    - -

    Nous avons ajouté un attribut class à l'élément <canvas> pour que ce soit plus facile à sélectionner dans le cas où nous aurions plusieurs canvas sur la page. Et nous avons supprimé les attributs width et height pour le moment (vous pouvez les remettre si vous le voulez mais nous les définirons en utilisant JavaScript dans une section plus bas). Les canvas sans hauteur et largeur explicites sont définits par défaut à 300 pixels par 150.

    -
  4. -
  5. -

    Maintenant, ajoutez les lignes suivantes à l'intérieur de l'élément {{htmlelement("script")}}:

    - -
    var canvas = document.querySelector('.myCanvas');
    -var width = canvas.width = window.innerWidth;
    -var height = canvas.height = window.innerHeight;
    - -

    Ici, nous avons stocké une référence vers le canvas dans la variable canvas. Sur la deuxième ligne, nous affectons à la fois une nouvelle variable width et la propriété width du canvas à {{domxref("Window.innerWidth")}} (ce qui nous donne la largeur de la fenêtre). Sur la troisième ligne, nos affectons à la fois une nouvelle variable height et la propriété height du canvas à {{domxref("Window.innerHeight")}} (ce qui nous donne la hauteur de la fenêtre). Nous avons donc un canvas qui remplit toute la largeur et hauteur de la fenêtre!

    - -

    Vous avez également vu que nous avons chaîné les assignations ensemble avec plusieurs signes égal — ce qui est autorié en JavaScript, et c'est une bonne technique si vous voulez que plusieurs variables aient la même valeur. Nous avons gardé la hauteur et largeur du canvas facilement accessibles dans les variables width/height, ces valeurs seront utiles plus tard (par exemple, si vous voulez dessiner quelque chose exactement à mi-chemin de la largeur du canvas).

    -
  6. -
  7. -

    Si vous sauvegardez et chargez votre exemple dans le navigateur maintenant, vous ne verrez rien, ce qui est normal, mais vous verrez également des barres de défilement, ce qui est un problème pour nous. Cela se produit parce que l'élément {{htmlelement("body")}} a des {{cssxref("margin")}} qui, ajoutées à la taille du canvas, résulte en un document qui est plus large que la fenêtre. Pour se débarasser des barres de défilement, nous devons supprimer les {{cssxref("margin")}} et aussi définir {{cssxref("overflow")}} à hidden. Ajoutez ce qui suit à l'intérieur du {{htmlelement("head")}} du document:

    - -
    <style>
    -  body {
    -    margin: 0;
    -    overflow: hidden;
    -  }
    -</style>
    - -

    Les barres de défilement ne devraient plus être là.

    -
  8. -
- -
-

Note : Vous devrez généralement définir la taille de l'image en utilisant les attributs HTML ou les propriétéss DOM, comme expliqué ci-dessus. Vous pourriez théoriquement utiliser CSS, le problème étant que le dimensionnement le canvas est alors effectué après que le contenu canvas n'ait été calculé, et comme toute autre image (puisque le canvas une fois affiché n'est plus qu'une simple image), elle peut devenir pixelisée/déformée.

-
- -

Obtenir le contexte du canvas et configuration finale

- -

Nous devons faire une dernière chose avant de considérer notre template finit. Pour dessiner sur le canvas, nous devons récupérer une référence à la zone de dessin, appelé un contexte. Pour ce faire, on utilise la méthode {{domxref("HTMLCanvasElement.getContext()")}}, qui, pour un usage basique ne prend qu'un seul paramètre, spécifiant quel type de contexte nous voulons récupérer.

- -

En l'occurence, nous voulons un canvas 2D, alors ajoutez le JavaScript suivant à la suite des autres instructions à l'intérieur de l'élément <script>:

- -
var ctx = canvas.getContext('2d');
- -
-

Note : Vous pouvez choisir d'autres types de contexte comme webgl pour WebGL, webgl2 pour WebGL 2, etc., mais nous n'en aurons pas besoin dans cet article.

-
- -

Voilà — notre canvas est maintenant préparé et prêt à être dessiné! La variable ctx contient désormais un objet {{domxref("CanvasRenderingContext2D")}}, et toutes les opérations de dessin sur le canvas impliqueront de manipuler cet objet.

- -

Faisons une dernière chose avant de passer à autre chose. Nous allons colorier l'arrière-plan du canvas en noir, cela vous donnera un avant-goût de l'API canvas. Ajoutez les lignes suivantes au bas de votre JavaScript:

- -
ctx.fillStyle = 'rgb(0, 0, 0)';
-ctx.fillRect(0, 0, width, height);
- -

Ici nous définissons une couleur de remplissage en utilisant la propriété du canvas {{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle")}} (qui prend une valeur de couleur tout comme les propriétés CSS), puis nous dessinons un rectangle qui recouvre intégralement la surface du canvas avec la méthode {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}} (les deux premiers paramètres sont les coordonnées du coin supérieur gauche du rectangle; les deux derniers sont la largeur et la hauteur du rectangle que vous voulez dessiner — on vous avait dit que ces variables width et height allaient être utiles)!

- -

OK, notre template est prêt et il est temps de passer à autre chose.

- -

Les bases du canvas 2D

- -

Pour rappel, toutes les opération de dessin sont effectuées en manipulant un objet {{domxref("CanvasRenderingContext2D")}} (dans notre cas, ctx).

- -

De nombreuses opérations doivent recevoir des coordonnées en entrée pour savoir où dessiner quelque chose — le coin supérieur gauche du canvas est le point (0, 0), l'axe horizontal (x) va de gauche à droite, et l'axe vertical (y) va de haut en bas.

- -

- -

Dessiner des formes est souvent fait en utilisant la forme rectangle, ou alors en traçant une ligne le long d'un certain chemin puis en remplissant la forme. Nous allons vous montrer ci-dessous comment faire ces deux choses.

- -

Rectangles simples

- -

Commençons avec quelques rectangles simples.

- -
    -
  1. -

    Tout d'abord, faites une copie de votre template (ou copiez localement le fichier 1_canvas_template.html si vous n'avez pas suivit les étapes précédentes).

    -
  2. -
  3. -

    Ensuite, ajoutez les lignes suivantes au bas de votre JavaScript:

    - -
    ctx.fillStyle = 'rgb(255, 0, 0)';
    -ctx.fillRect(50, 50, 100, 150);
    - -

    Si vous sauvegardez votre code et rafraichissez la page, vous devriez voir qu'un rectangle rouge est apparu sur le canvas. Son coin supérieur gauche est à (50,50) pixels du coin supérieur gauche du canvas (comme définit par les deux premiers paramètres), il a une largeur de 100 pixels et une hauteur de 150 pixels (comme définit par les paramètres 3 et 4).

    -
  4. -
  5. -

    Ajoutons un autre rectangle dans le mix — un vert cette fois. Ajoutez ce qui suit au bas de votre JavaScript:

    - -
    ctx.fillStyle = 'rgb(0, 255, 0)';
    -ctx.fillRect(75, 75, 100, 100);
    - -

    Sauvegardez et rafraichissez, et vous verrez un nouveau rectangle. Cela soulève un point important: les opérations graphiques comme dessiner des rectangles, lignes, et autres, sont executées dans l'ordre dans lequel elle apparaissent dans le code. Pensez-y comme peindre un mur, chaque couche de peinture s'ajoute par dessus les anciennes et peuvent même mettre cacher ce qu'il y a en dessous. Vous ne pouvez rien y faire, il faut donc réfléchir soigneusement à l'ordre dans lequel vous allez dessiner les éléments graphiques.

    -
  6. -
  7. -

    Notez que vous pouvez dessiner des éléments graphiques semi-transparents en spécifiant une couleur semi-transparente, par exemple en utilisant rgba(). La valeur a définit ce qu'on appelle le "canal alpha", ou la quantité de transparence de la couleur. Plus la valeur de a est élevée, plus la couleur est opaque. Ajoutez ce qui suit à votre code:

    - -
    ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
    -ctx.fillRect(25, 100, 175, 50);
    -
  8. -
  9. -

    Maintenant essayez de dessiner plus de rectangles par vous-même; amusez-vous!

    -
  10. -
- -

Traits et épaisseurs de ligne

- -

Jusqu'à présent nous avons vu comment dessiner des rectangles pleins, mais on peut aussi ne dessiner que les contours (dit strokes - traits - en graphic design). Pour définir la couleur que vous voulez pour le contour, utilisez la propriété {{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle")}}. Pour dessiner le contour du rectangle, on appelle {{domxref("CanvasRenderingContext2D.strokeRect", "strokeRect")}}.

- -
    -
  1. -

    Ajoutez ce qui suit au bas de votre JavaScript:

    - -
    ctx.strokeStyle = 'rgb(255, 255, 255)';
    -ctx.strokeRect(25, 25, 175, 200);
    -
  2. -
  3. -

    L'épaisseur de trait par défaut est de 1 pixel; vous pouvez ajuster la valeur de la propriété {{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth")}} pour changer ça (prend un nombre spécifiant le nombre de pixels d'épaisseur de trait). Ajoutez la ligne suivante entre les deux lignes précédentes:

    - -
    ctx.lineWidth = 5;
    - -

    Vous devriez maintenant voir que votre contour blanc est devenu beaucoup plus épais!

    -
  4. -
- -

C'est tout pour le moment. À ce stade votre exemple devrait ressembler à ça:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles.html", '100%', 250)}}

- -
-

Note : Le code terminé est disponible sur GitHub, 2_canvas_rectangles.html.

-
- -

Dessiner des chemins

- -

Si vous voulez dessiner quelque chose de plus complexe qu'un rectangle, vous allez certainement devoir utiliser un path (chemin). En gros, cela implique de spécifier exactement où déplacer un stylo sur le canvas pour tracer la forme que vous voulez. L'API Canvas inclut des fonctions pour dessiner des lignes droites, des cercles, des courbes Bézier, et plus encore.

- -

Commençons la section en faisant une nouvelle copie de notre template (1_canvas_template.html), dans lequel nous allons dessiner le nouvel exemple.

- -

Nous allons utiliser quelques méthodes et propriétés communes dans les sections suivantes:

- - - -

Typiquement, une manière de dessiner un trait simple ressemblerait à ça:

- -
ctx.fillStyle = 'rgb(255, 0, 0)';
-ctx.beginPath();
-ctx.moveTo(50, 50);
-// tracer le trait
-ctx.fill();
- -

Dessiner des lignes

- -

Dessinons un triangle équilatéral sur le canvas.

- -
    -
  1. -

    Tout d'abord, ajoutez la fonction d'aide suivante au bas de votre code. Elle convertit des valeurs en degrés en radians, ce qui est utile car chaque fois que vous devez fournir une valeur d'angle en JavaScript, ce sera presque toujours en radians, tandis que les humains pensent généralement en degrés.

    - -
    function degToRad(degrees) {
    -  return degrees * Math.PI / 180;
    -};
    -
  2. -
  3. -

    Ensuite, commencez votre path en ajoutant ce qui suit au bas de votre JavaScript. Ici, nous définissons une couleur pour notre triangle et déplaçons le stylo au point (50, 50) sans rien tracer. C'est à partir de là que nous allons dessiner notre triangle.

    - -
    ctx.fillStyle = 'rgb(255, 0, 0)';
    -ctx.beginPath();
    -ctx.moveTo(50, 50);
    -
  4. -
  5. -

    Maintenant ajoutez le bloc de code suivant:

    - -
    ctx.lineTo(150, 50);
    -var triHeight = 50 * Math.tan(degToRad(60));
    -ctx.lineTo(100, 50+triHeight);
    -ctx.lineTo(50, 50);
    -ctx.fill();
    - -

    Parcourons ceci dans l'ordre:

    - -
      -
    1. -

      D'abord nous ajoutons une ligne vers (150, 50) — notre path va maintenant 100 pixels vers la droite le long de l'axe horizontal (x).

      -
    2. -
    3. -

      Puis, nous calculons la hauteur de notre triangle équilatéral, en utilisant un peu de trigonométrie simple. Nous dessinons un triangle pointant vers le bas.

      - -

      Les angles d'un triangle équilatéral sont tous de 60 degrés. Pour calculer la hauteur, nous pouvons séparer le triangle en deux triangles rectangles par le milieu, qui auront alors des angles de 90, 60 et 30 degrés.

      - -

      Pour ce qui est des côtés:

      - -
        -
      • Le côté le plus long est appelé l'hypoténuse
      • -
      • Le côté relié à l'angle de 60 degrés (et qui n'est pas l'hypothénuse) est dit adjacent à cet angle — sa longueur est de 50 pixels puisque c'est la moitié de la ligne que nous avons dessiné.
      • -
      • Le côté opposé à l'angle de 60 degrés est dit opposé à cet angle — c'est la hauteur que nous voulons calculer.
      • -
      - -

       

      - -

      - -

      Une des formule trigonométrique de base stipule que la longueur du côté adjacent mutiplié par la tangente de l'angle est égale à l'opposé, soit 50 * Math.tan(degToRad(60)). Nous utilisons notre fonction degToRad() pour convertir 60 degrés en radians, puisque {{jsxref("Math.tan()")}} attend une valeur en radians.

      -
    4. -
    5. -

      Avec la hauteur calculée, nous ajoutons une nouvelle ligne vers (100, 50+triHeight). La coordonnée X est simple, elle est à mi-chemin de la ligne que nous avons tracé. La valeur de Y d'autre part doit être de 50 plus la hauteur du triangle, puisque le haut du triangle est à 50 pixels du haut du canvas.

      -
    6. -
    7. -

      L'instruction qui suit ajoute une ligne vers le point de départ du triangle.

      -
    8. -
    9. -

      Pour finir, nous appelons ctx.fill() pour finir le path et remplir la forme.

      -
    10. -
    -
  6. -
- -

Dessiner des cercles

- -

Maintenant, voyons comment dessiner un cercle sur le canvas. Pour ce faire, on utilise la méthode {{domxref("CanvasRenderingContext2D.arc", "arc()")}}, qui dessine tout ou une portion de cercle à un point spécifié.

- -
    -
  1. -

    Ajoutons un arc à notre canvas en ajoutant le code qui suit:

    - -
    ctx.fillStyle = 'rgb(0, 0, 255)';
    -ctx.beginPath();
    -ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false);
    -ctx.fill();
    - -

    arc() prend six paramètres.

    - -
      -
    • Les deux premiers spécifient la position du centre du cercle (X et Y respectivement).
    • -
    • Le troisième est le rayon du cercle
    • -
    • Le quatrième et le cinquième sont les angles de début et de fin pour dessiner l'arc (donc spécifier 0 et 360 nous donne un cercle fermé)
    • -
    • Et le sixième paramètre définit si le cercle doit être dessiné dans le sens des aiguilles d'une montre ou dans le sens inverse (false pour le sens horaire).
    • -
    - -
    -

    Note : 0 degrés est horizontalement vers la droite.

    -
    -
  2. -
  3. -

    Ajoutons un autre arc:

    - -
    ctx.fillStyle = 'yellow';
    -ctx.beginPath();
    -ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true);
    -ctx.lineTo(200, 106);
    -ctx.fill();
    - -

    Le motif ici est très similaire, a deux différences près:

    - -
      -
    • Nous avons mis le dernier paramètre de arc() à true, ce qui signifie que l'arc est tracé dans le sens inverse des aiguilles d'une montre.  Donc si notre arc commence à -45 degrés et fini à 45 degrés, nous dessinons un arc de 270 degrés. Si vous changez true à false et ré-exécutez le code, seule une portion de 90 degrés sera dessinée.
    • -
    • Avant d'appeler fill(), nous ajoutons une ligne vers le centre du cercle. Nous obtenons une découpe de style Pac-Man plutôt sympa. Si vous supprimiez cette ligne (essayez!) et ré-exécutiez le code, vous auriez juste un cercle dont le bout a été coupé — entre le début et la fin de l'arc. Cela illuste un autre point important du canvas: si vous essayez de remplir une forme incomplète (qui n'est pas fermée), le navigateur ajoute une ligne droite entre le début et la fin du path et le remplit.
    • -
    -
  4. -
- -

C'est tout pour le moment; votre exemple final devrait ressembler à ceci:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths.html", '100%', 200)}}

- -
-

Note : Le code finit est disponible sur GitHub, 3_canvas_paths.html.

-
- -
-

Note : Pour en savoir plus sur les fonctions de dessin avancées, telles que les courbes Bézier, consultez notre tutoriel Dessiner des formes avec le canevas.

-
- -

Texte

- -

Canvas dispose également de fonctionnalités pour écrire du texte. Nous allons les explorer brièvement. Commencez par créer une nouvelle copie du template (1_canvas_template.html), dans lequel nous allons dessiner le nouvel exemple.

- -

Le texte peut être avec deux méthodes:

- - - -

Ces deux méthodes prennent trois paramètres: la chaîne de caractères à écrire et les coordonnées X et Y du coin supérieur gauche de la zone de texte (text box) — littéralement, la zone entourant le texte que vous écrivez.

- -

Il existe également un certain nombre de proprétés pour contrôler le rendu du texte, comme {{domxref("CanvasRenderingContext2D.font", "font")}}, qui permer de spécifier la police d'écriture, la taille, etc — elle accepte la même syntaxe que la propriété CSS {{cssxref("font")}}.

- -

Essayez d'ajouter le bloc suivant au bas de votre javaScript:

- -
ctx.strokeStyle = 'white';
-ctx.lineWidth = 1;
-ctx.font = '36px arial';
-ctx.strokeText('Canvas text', 50, 50);
-
-ctx.fillStyle = 'red';
-ctx.font = '48px georgia';
-ctx.fillText('Canvas text', 50, 150);
- -

Ici nous dessinons deux lignes de texte, une avec le contour et l'autre remplie. L'exemple final devrait ressembler à ça:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text.html", '100%', 180)}}

- -
-

Note : Le code final est disponible sur GitHub, 4_canvas_text.html.

-
- -

Jouez avec et voyez ce que vous pouvez faire! Vous pouvez trouver plus d'information sur les options disponibles pour ajouter du texte sur un canvas dans Dessin de texte avec canvas.

- -

Dessiner des images sur le canvas

- -

Il est possible d'afficher des images externes sur le canvas. Ce peut être des images simples, des images à l'intérieur d'une vidéo, ou le contenu d'autres canvas. Pour le moment, nous allons juste nous occuper d'ajouter des images simples sur le canvas.

- -
    -
  1. -

    Comme précédemment, créez une nouvelle copie du template (1_canvas_template.html), où nous dessinerons l'exemple. Vous allez également devoir sauvegarder une copie de notre image d'exemple — firefox.png — dans le même répertoire.

    - -

    Les images sont dessinés sur le canvas en utilisant la méthode {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}}. Dans sa version la plus simple, elle prend trois paramètres — une référence de l'image que vous voulez afficher et les coordonnées X et Y du coin supérieur gauche de l'image sur le canvas.

    -
  2. -
  3. -

    Commençons par obtenir une ressource de l'image à inclure dans notre canvas. Ajoutez les lignes suivantes au bas de votre JavaScript:

    - -
    var image = new Image();
    -image.src = 'firefox.png';
    - -

    Ici, nous créons un nouvel objet {{domxref("HTMLImageElement")}} en utilisant le constructeur {{domxref("HTMLImageElement.Image()", "Image()")}}. (L'objet retourné est le même type que celui retourné lorsque vous récupérez une référence vers un élément {{htmlelement("img")}} existant). Nous définissons son attribut  {{htmlattrxref("src", "img")}} à notre image du logo Firefox. À ce stade, le navigateur commence à charger l'image.

    -
  4. -
  5. -

    Nous pourrions essayer maintenant d'inclure l'image en utilisant drawImage(), mais nous devons nous assurer que le fichier image ait été chargé en premier, faute de quoi le code échouera. Nous pouvons y parvenir en utilisant le gestionnaire d'événement onload, qui ne sera appelé que lorsque l'image aura fini de charger. Ajoutez le bloc suivant à la suite du précédent:

    - -
    image.onload = function() {
    -  ctx.drawImage(image, 50, 50);
    -}
    - -

    Si vous chargez votre exemple dans le navigateur maintenant, vous devriez voir l'image inclue dans le canvas.

    -
  6. -
  7. -

    Mais il y en a plus! Et si nous ne voulions afficher qu'une partie de l'image, ou la redimensionner? Nous pouvons faire ces deux choses avec une version plus complexe de drawImage(). Mettez à jour votre ligne ctx.drawImage() comme suit:

    - -
    ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175);
    - -
      -
    • Le premier paramètre est la référence de l'image, comme précédemment.
    • -
    • Les paramètres 2 et 3 définissent les coordonnées à partir d'où découper l'image, relativement au coin supérieur gauche de l'image d'origine. Tout ce qui est à gauche de X (paramètre 2) ou au-dessus de Y (paramètre 3) ne sera pas dessiné.
    • -
    • Les paramètres 4 et 5 définissent la largeur et hauteur de la zone que nous voulons découper, à partir du coin supérieur gauche de l'image découpée.
    • -
    • Les paramètres 6 et 7 définissent les coordonnées où vous souhaitez placer l'image sur le canvas, relativement au coin supérieur gauche du canvas.
    • -
    • Les paramètres 8 et 9 définissent la largeur et la hauteur affichée de l'image découpée. En l'occurence, nous avons spécifié les mêmes dimensions que la découpe, mais vous pouvez la redimensionner (et la déformer) en spécifiant des valeurs différentes.
    • -
    -
  8. -
- -

L'exemple final devrait ressembler à ça:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images.html", '100%', 260)}}

- -
-

Note : Le code final est disponible sur GitHub, 5_canvas_images.html.

-
- -

Boucles et animations

- -

Jusqu'ici, nous avons couvert quelques utilisations très basiques du canvas 2D, mais vous ne ressentirez la pleine puissance du canvas que si vous le mettez à jour ou l'animez d'une manière ou d'une autre. Après tout, le canvas fournit des images scriptables! Si vous n'avez pas l'intention de changer quelque chose, alors autant utiiliser des images statiques et vous épargner du travail.

- -

Créer une boucle

- -

Jouer avec des boucles est plutôt amusant — vous pouvez exécuter des commandes de canvas à l'intérieur d'une boucle for (ou tout autre type de boucle) comme n'importe quel autre code JavaScript.

- -

Construisons un exemple simple.

- -
    -
  1. -

    Créez une nouvelle copie du template (1_canvas_template.html) et ouvrez-le dans votre éditeur de texte.

    -
  2. -
  3. -

    Ajoutez la ligne qui suit au bas de votre JavaScript. Elle contient une nouvelle méthode, {{domxref("CanvasRenderingContext2D.translate", "translate()")}}, qui déplace le point d'origine du canvas:

    - -
    ctx.translate(width/2, height/2);
    - -

    Cela a pour effet de déplacer l'origine des coordonnées (0, 0) au centre du canvas, plutôt que d'être dans le coin supérieur gauche. C'est très utile dans de nombreuses situations, comme celle-ci, où nous voulons que notre dessin soit dessiné par rapport au centre du canvas.

    -
  4. -
  5. -

    Maintenant ajoutez le code suivant au bas du Javacript:

    - -
    function degToRad(degrees) {
    -  return degrees * Math.PI / 180;
    -};
    -
    -function rand(min, max) {
    -  return Math.floor(Math.random() * (max-min+1)) + (min);
    -}
    -
    -var length = 250;
    -var moveOffset = 20;
    -
    -for(var i = 0; i < length; i++) {
    -
    -}
    - -

    Ici, nous implémentons

    - -
      -
    • la même fonction degToRad() que nous avons vu dans l'exemple du triangle auparavant,
    • -
    • une fonction rand(), qui retoune un nombre aléatoire entre une limite inférieure et une limite supérieure,
    • -
    • les variables length et moveOffset (que nous verrons plus loin),
    • -
    • et une boucle for vide.
    • -
    -
  6. -
  7. -

    L'idée est que nous allons dessiner quelque chose sur le canvas à l'intérieur de la boucle for, et itérer dessus pour créer quelque chose d'intéressant. Ajoutez le code suivant à l'intérieur de la boucle for:

    - -
    ctx.fillStyle = 'rgba(' + (255-length) + ', 0, ' + (255-length) + ', 0.9)';
    -ctx.beginPath();
    -ctx.moveTo(moveOffset, moveOffset);
    -ctx.lineTo(moveOffset+length, moveOffset);
    -var triHeight = length/2 * Math.tan(degToRad(60));
    -ctx.lineTo(moveOffset+(length/2), moveOffset+triHeight);
    -ctx.lineTo(moveOffset, moveOffset);
    -ctx.fill();
    -
    -length--;
    -moveOffset += 0.7;
    -ctx.rotate(degToRad(5));
    - -

    Ainsi à chaque itération, on:

    - -
      -
    1. Définit fillStyle comme étant une nuance de violet légèrement transparente, et qui change à chaque fois en fonction de la valeur de length. Comme vous le verrez plus tard, sa valeur diminue à chaque itération, ce qui a pour effet de rendre la couleur toujours plus claire.
    2. -
    3. Ouvre un path.
    4. -
    5. Déplace le stylo aux coordonnées de (moveOffset, moveOffset); Cette variable définit jusqu'où nous voulons nous déplacer à chaque fois que nous dessinons.
    6. -
    7. Dessine une ligne aux coordonées de (moveOffset+length, moveOffset). Cela dessine une ligne de longueur length parallèle à l'axe X.
    8. -
    9. Calcule la hauteur du triangle, comme vu auparavant.
    10. -
    11. Dessine une ligne vers le coin du bas du triangle.
    12. -
    13. Dessine une ligne vers le début du triangle.
    14. -
    15. Appelle fill() pour remplir le triangle.
    16. -
    17. Met à jour les variables qui indiquent comment dessiner le triangle, afin qu'elles soient prêtes pour la prochaine itération: -
        -
      • Diminue la valeur de length de 1, de sorte que les triangles deviennent de plus en plus petits;
      • -
      • Augmente un peu moveOffset pour que chaque triangle successif soit légèrement plus éloigné;
      • -
      • et utilise une nouvelle fonction, {{domxref("CanvasRenderingContext2D.rotate", "rotate()")}}, qui permet de faire pivoter entièrement le canvas! Nous le faisons pivoter de 5 degrés avant de dessiner le triangle suivant.
      • -
      -
    18. -
    -
  8. -
- -

C'est tout! L'exemple final devrait ressemble à ça:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop.html", '100%', 550)}}

- -

À ce stade, nous vous encourageons à jouer avec l'exemple et de vous l'approprier! Par exemple:

- - - -
-

Note : Le code terminé est disponible sur GitHub, 6_canvas_for_loop.html.

-
- -

Animations

- -

L'exemple de boucle que nous avons construit ci-dessus était amusant, mais en vrai vous avez besoin d'une boucle qui continue constamment d'itérer pour toute application sérieuse de canvas (telles que les jeux et les visualisations en temps réel). Si vous pensez à votre canvas comme étant en quelque sorte un film, vous allez vouloir que l'affichage se mette à jour à chaque image pour afficher la mise à jour avec un taux de rafraichissement idéal de 60 images par seconde, afin que le mouvement soit lisse et agréable pour l'oeil humain.

- -

Il y a quelques fonctions JavaScript qui vous permettrons d'exécuter des fonctions de manière répétée, plusieurs fois par seconde, la meilleure étant ici {{domxref("window.requestAnimationFrame()")}}. Elle prend un paramètre — la fonction que vous voulez exécuter pour chaque image. Dès que le navigateur est prêt à mettre à jour l'écran, votre fonction sera appelée. Si cette fonction dessine la nouvelle mise à jour, puis appelle de nouveau requestAnimationFrame() juste avant la fin de la fonction, la boucle d'animation continuera de s'exécuter de manière fluide. La boucle s'arrête lorsque vous vous arrêtez d'appeler requestAnimationFrame() ou si vous appelez {{domxref("window.cancelAnimationFrame()")}} après avoir appelé requestAnimationFrame() mais avant que votre fonction n'ait été exécutée.

- -
-

Note: C'est une bonne pratique d'appeler cancelAnimationFrame() à partir de votre code principal lorsque vous avez terminé d'utiliser l'animation, pour vous assurer qu'aucune mise à jour n'attend d'être exécutée.

-
- -

Le navigateur s'occupe des détails complexes tels qu'exécuter l'animation à une vitesse constante, et ne pas gaspiller de ressources en animant des choses qui ne sont pas visibles.

- -

Pour voir comment cela fonctionne, regardons rapidement notre exemple des balles qui rebondissent (le voir en direct, et voir le code source). Le code de la boucle qui garde le tout en mouvement ressemble à ceci:

- -
function loop() {
-  ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
-  ctx.fillRect(0, 0, width, height);
-
-  while(balls.length < 25) {
-    var ball = new Ball();
-    balls.push(ball);
-  }
-
-  for(i = 0; i < balls.length; i++) {
-    balls[i].draw();
-    balls[i].update();
-    balls[i].collisionDetect();
-  }
-
-  requestAnimationFrame(loop);
-}
-
-loop();
- -

Nous lançons la fonction loop() une fois pour commencer le cycle et dessiner la première image de l'animation. La fonction loop() s'occupe ensuite d'appeler requestAnimationFrame(loop) pour afficher la prochaine image de l'animation, et ce continuellement.

- -

Notez que sur chaque image, nous effaçons complètement le canvas et redessinons tout. Nous créons de nouvelles balles pour chaque image — au maximum 25 — puis, pour chaque balle, la dessinons, mettons à jour sa position, et vérifions si elle est en collision avec une autre balle. Une fois que vous avez dessiné quelque chose sur un canvas, il n'y a aucun moyen pour manipuler cet élément graphique individuellement comme vous pouvez le faire avec les élément DOM. Vous ne pouvez pas déplacer les balles sur le canvas parce qu'une fois dessinée, une balle n'est plus une balle mais juste des pixels sur un canvas. Au lieu de ça, vous devez effacer et redessiner, soit en effaçant et redessinant absolutement tout le canvas, soit en ayant du code qui sait exactement quelles parties doivent être effacées, et qui efface et redessine uniquement la zone minimale nécessaire.

- -

Optimiser l'animation graphique est une spécialité entière de programmation, avec beaucoup de techniques ingénieuses disponibles. Mais celles-ci sont au-delà de ce dont nous avons besoin pour notre exemple!

- -

En général, le processus pour animer un canvas implique les étapes suivantes:

- -
    -
  1. Effacer le contenu du cavas (par exemple avec {{domxref("CanvasRenderingContext2D.fillRect", "fillRect()")}} ou {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}}).
  2. -
  3. Sauvegarder l'état (si nécessaire) en utilisant {{domxref("CanvasRenderingContext2D.save", "save()")}} — c'est nécessaire lorsque vous voulez enregistrer les paramètres que vous mis à jour sur le canvas avant de continuer, ce qui est utile pour des applications plus avancées.
  4. -
  5. Dessiner les éléments graphiques que vous animez.
  6. -
  7. Restaurer les paramètres sauvegardés à l'étape 2 en utilisant {{domxref("CanvasRenderingContext2D.restore", "restore()")}}
  8. -
  9. Appeler requestAnimationFrame() pour planifier le dessin de l'image suivante de l'animation.
  10. -
- -
-

Note : Nous ne couvrirons pas save() et restore() ici, mais elles sont bien expliquées dans notre tutoriel Transformations (et ceux qui le suivent).

-
- -

Une animation simple de personnage

- -

Créons maintenant notre propre animation simple — nous allons faire parcourir l'écran à un personnage d'un certain jeu vidéo rétro plutôt génial.

- -
    -
  1. -

    Faites une nouvelle copie du template (1_canvas_template.html) et ouvrez-le dans votre éditeur de texte. Sauvegardez une copie de walk-right.png dans le même répertoire.

    -
  2. -
  3. -

    Au bas du JavaScript, ajoutez la ligne suivante pour placer une fois de plus l'origine des coordonnées au milieu du canvas:

    - -
    ctx.translate(width/2, height/2);
    -
  4. -
  5. -

    Nous allons maintenant créer un objet {{domxref("HTMLImageElement")}}, définir son attribut {{htmlattrxref("src", "img")}} à l'image que nous voulons charger, et ajouter le gestionnaire d'événement onload pour appeler la fonction draw() quand l'image sera chargée:

    - -
    var image = new Image();
    -image.src = 'walk-right.png';
    -image.onload = draw;
    -
  6. -
  7. -

    Ajoutons quelques variables pour garder une trace de la position du sprite à dessiner à l'écran, et le numéro du sprite que nous voulons afficher.

    - -
    var sprite = 0;
    -var posX = 0;
    - -

    L'image de sprites (que nous avons respectueusement emprunté à Mike Thomas dans son article Create a sprite sheet walk cycle using using CSS animation — créer un cycle de marche avec une feuille de sprites en utilisant les animations CSS) ressemble à ça:

    - -

    - -

    Elle contient six sprites qui constituent une séquence de marche — chacun a 102 pixels de large et 148 pixels de hauteur. Pour afficher chaque sprite proprement, nous allons devoir utiliser drawImage() pour découper un seul sprite de l'image et n'afficher que cette partie, comme nous l'avons fait précédemment avec le logo Firefox. La coordonnée X de la découpe devra être un multiple de 102 et la coordonnée Y sera toujours 0. La taille de la découpe sera toujours de 102 pixels par 148.

    -
  8. -
  9. -

    Insérons maintenant une fonction draw() vide au bas du code, prête à être remplie de code:

    - -
    function draw() {
    -
    -};
    -
  10. -
  11. -

    Le reste du code dans cette section va dans draw(). Tout d'abord, ajoutez la ligne suivante, qui efface le canvas pour préparer le dessin de chaque image. Notez que nous devons spécifier le coin supérieur gauche du rectangle comme étant -(width/2), -(height/2) puisque nous avons définit l'origine du canvas à width/2, height/2 plus tôt.

    - -
    ctx.fillRect(-(width/2), -(height/2), width, height);
    -
  12. -
  13. -

    Ensuite, nous allons dessinons notre image en utilisant drawImage() — la version à 9 paramètres. Ajoutez ce qui suit:

    - -
    ctx.drawImage(image, (sprite*102), 0, 102, 148, 0+posX, -74, 102, 148);
    - -

    Comme vous pouvez le voir:

    - -
      -
    • Nous spécifions image comme étant l'image à inclure.
    • -
    • Les paramètres 2 et 3 spécifient le coin supérieur gauche de la portion de l'image à découper: la valeur X vaut sprite multiplié par 102 (où sprite est le numéro du sprite, entre 0 et 5) et la valeur Y vaut toujours 0.
    • -
    • Les paramètres 4 et 5 spécifient la taille de la découpe — 102 pixels par 148.
    • -
    • Les paramètres 6 et 7 spécifient le coin supérieur gauche de la découpe sur le canvas — la position de X est 0+posX, ce qui signifie que nous pouvons modifier la position du dessin en modifiant la valeur de posX.
    • -
    • Les paramètres 8 et 9 spécifient la taille de l'image sur le canvas. Nous voulons garder sa taille d'origine, on définit donc 102 pour largeur et 148 pour hauteur.
    • -
    -
  14. -
  15. -

    Maintenant, nous allons changer la valeur de sprite après chaque dessin — après certains d'entre eux du moins. Ajoutez le bloc de code suivant au bas de la fonction draw():

    - -
      if (posX % 13 === 0) {
    -    if (sprite === 5) {
    -      sprite = 0;
    -    } else {
    -      sprite++;
    -    }
    -  }
    - -

    Nous enveloppons le bloc entier de if (posX % 13 === 0) { ... }. On utilise l'opérateur modulo (%) (aussi connu sous le nom d'opérateur reste) pour vérifier si la valeur de posX peut être exactement divisée par 13, sans reste. Si c'est le cas, on passe au sprite suivant en incrémentant sprite (en retournant à 0 après le sprite #5). Cela implique que nous ne mettons à jour le sprite que toutes les 13èmes images, ou à peu près 5 images par seconde (requestAnimationFrame() appelle jusqu'à 60 images par secondes si possible). Nous ralentissons délibéremment le cadence des images parce que nous n'avons que six sprites avec lesquels travailler, et si on en affiche un à chaque 60ième de seconde, notre personnage va bouger beaucoup trop vite!

    - -

    À l'intérieur du bloc, on utilise une instruction if ... else pour vérifier si la valeur de sprite vaut 5 (le dernier sprite, puisque les numéro de sprite vont de 0 à 5). Si nous sommes en train d'afficher le dernier sprite, alors on réinitialilse sprite à 0; sinon on l'incrémente de 1.

    -
  16. -
  17. -

    Ensuite, nous devons déterminer comment modifier la valeur de posX sur chaque image — ajoutez le bloc de code à la suite:

    - -
      if(posX > width/2) {
    -    newStartPos = -((width/2) + 102);
    -    posX = Math.ceil(newStartPos / 13) * 13;
    -    console.log(posX);
    -  } else {
    -    posX += 2;
    -  }
    - -

    Nous utilisons une autre instruction if ... else pour voir si la valeur de posX est plus grande que width/2, ce qui signifie que notre personnage est sortit du bord droit de l'écran. Si c'est le cas, on calcule la position qui met le personnage à gauche du bord gauche de l'écran, et on définit posX au multiple de 13 le plus proche de ce nombre. Il faut obligatoirement un multiple de 13 pour que le bloc de code précédent puisse toujours fonctionner!

    - -

    Si notre personnage n'a pas atteint le bord de l'écran, on incrémente posX de 2. Cela le fera bouger un peu vers la droite la prochaine fois qu'on le dessinera.

    -
  18. -
  19. -

    Finalement, nous devons boucler sur l'animation en appelannt {{domxref("window.requestAnimationFrame", "requestAnimationFrame()")}} en bas de la fonction draw():

    - -
    window.requestAnimationFrame(draw);
    -
  20. -
- -

Et voilà! L'exemple final devrait ressembler à ça:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}

- -
-

Note : Le code final est disponible sur GitHub, 7_canvas_walking_animation.html.

-
- -

Une application simple de dessin

- -

Comme exemple final d'animation, nous aimerions vous montrer une application très simple de dessin, pour illustrer comment la boucle d'animation peut être combinée avec les entrées de l'utilisateur (comme le mouvement de la souris en l'occurence). Nous n'allons pas expliquer la procédure pas à pas pour construire cette application, nous allons juste explorer les parties les plus intéressantes du code.

- -

L'exemple peut être trouvé sur GitHub, 8_canvas_drawing_app.html, et vous pouvez jouer avec en direct ci-dessous:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app.html", '100%', 600)}}

- -

Regardons les parties les plus intéressantes. Tout d'abord, nous gardons une trace des coordonnées X et Y de la souris et si elle est pressée ou non grâce à trois variables: curX, curY, et pressed. Quand la souris bouge, nous déclenchons une fonction via le gestionnaire d'événement onmousemove, lequel capture les valeurs X et Y actuelles. Nous utilisons également les gestionnaires d'événement onmousedown et onmouseup pour changer la valeur de pressed à true quand le bouton de la souris est pressé, et de nouveau à false quand il est relaché.

- -
var curX;
-var curY;
-var pressed = false;
-
-document.onmousemove = function(e) {
-  curX = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
-  curY = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
-}
-
-canvas.onmousedown = function() {
-  pressed = true;
-};
-
-canvas.onmouseup = function() {
-  pressed = false;
-}
- -

Quand le bouton "Clear canvas" (effacer le canvas) est cliqué, nous exécutons une simple fonction qui efface entièrement le canvas grâce à un rectangle noir, de la même manière que nous avons vu précédemment:

- -
clearBtn.onclick = function() {
-  ctx.fillStyle = 'rgb(0, 0, 0)';
-  ctx.fillRect(0, 0, width, height);
-}
- -

La boucle du dessin est relativement simple cette fois-ci — si pressed est à true, nous dessinons un cercle rempli avec la couleur du color picker (sélecteur de couleur), et d'un rayon égal à la valeur définit dans le champs de sélection dans un intervalle.

- -
function draw() {
-  if(pressed) {
-    ctx.fillStyle = colorPicker.value;
-    ctx.beginPath();
-    ctx.arc(curX, curY-85, sizePicker.value, degToRad(0), degToRad(360), false);
-    ctx.fill();
-  }
-
-  requestAnimationFrame(draw);
-}
-
-draw();
- -
-

Note : Les types d'{{htmlelement("input")}} range et color sont relativement bien pris en charge parmi les différents navigateurs, à l'exception des versions d'Internet Explorer inférieures à 10; et Safari ne prend pas en charge le type color. Si votre navigateur ne prend pas en charge ces types, il affichera simplement un champ texte et vous n'aurez qu'à entrer des valeurs de couleur et numéro valides vous-mêmes.

-
- -

WebGL

- -

Il est maintenant temps de laisser la 2D derrière, et de jeter un coup d'oeil au canvas 3D. Le contenu 3D d'un canvas est spécifié en utilisant l'API WebGL, qui est une API complètement séparée de l'API des canvas 2D, même si ls deux sont affichés sur des éléments {{htmlelement("canvas")}}.

- -

WebGL est basé sur le langage de programmation graphique OpenGL, et permet de communiquer directement avec le GPU de l'ordinateur. En soi, l'écriture WebGL est plus proche des langages de bas niveau tel que C++ que du JavaScript usuel; c'est assez complexe mais incroyablement puissant.

- -

Utiliser une bibliothèque

- -

De par sa complexité, la plupart des gens écrivent du code de graphique 3D en utilisant une bibliothèque JavaScript tierce telle que Three.js, PlayCanvas ou Babylon.js. La plupart d'entre elles fonctionnent d'une manière similaire, offrant des fonctionnalités pour créer des formes primitives et personnalisées, positionner des caméras et des éclairages, recouvrir des surfaces avec des textures et plus encore. Elles se chargent de WebGL pour vous, vous permettant de travailler à un niveau plus haut.

- -

Oui, en utiliser une signifie apprendre une autre nouvelle API (une API tierce en l'occurence), mais elles sont beaucoup plus simples que de coder du WebGL brut.

- -

Recréer notre cube

- -

Regardons un exemple simple pour créer quelque chose avec une bibliothèque WebGL. Nous allons choisir Three.js, puisque c'est l'une des plus populaires. Dans ce tutoriel, nous allons créer le cube 3D qui tourne que nous avons plus tôt.

- -
    -
  1. -

    Pour commencer, créez une copie locale de index.html dans un nouveau répertoire, et sauvegardez metal003.png dans ce même répertoire. C'est l'image que nous allons utiliser comme texture de surface du cube plus tard.

    -
  2. -
  3. -

    Ensuite, créez un nouveau fichier main.js, toujours dans le même répertoire.

    -
  4. -
  5. -

    Si vous ouvrez index.html dans votre éditeur de texte, vous verrez qu'il y a deux éléments {{htmlelement("script")}} — le premier ajoute three.min.js à la page, et le second ajoute notre fichier main.js à la page. Vous devez télécharger la bibliothèque three.min.js et la sauvegarder dans le même répertoire que précédemment.

    -
  6. -
  7. -

    Maintenant que nous avons inclus three.js dans notre page, nous pouvons commencer à écrire du code JavaScript qui l'utilise dans main.js. Commençons par créer une nouvelle scène — ajoutez ce qui suit dans le fichier main.js:

    - -
    var scene = new THREE.Scene();
    - -

    Le constructeur Scene() crée une nouvelle scène, qui représente l'ensemble du monde 3D que nous essayons d'afficher.

    -
  8. -
  9. -

    Ensuite, nous avons besoin d'une caméra pour voir la scène. En terme d'imagerie 3D, la caméra représente la position du spectateur dans le monde. Pour ajouter une caméra, ajoutez les lignes suivantes à la suite:

    - -
    var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    -camera.position.z = 5;
    -
    - -

    Le constructeur PerspectiveCamera() prend quatre arguments:

    - -
      -
    • Le champ de vision: Quelle est la largeur de la zone devant la caméra qui doit être visible à l'écran, en degrés.
    • -
    • Le rapport d'aspect (aspect ratio): Habituellement, c'est le rapport entre la largeur de la scène divisé par la hauteur de la scène. Utiliser une autre valeur va déformer la scène (ce qui pourrait être ce que vous voulez, mais ce n'est généralement pas le cas).
    • -
    • Le plan proche (near plane): Jusqu'où les objets peuvent être proches de la caméra avant que nous arrêtions de les afficher à l'écran. Pensez-y comme quand vous approchez votre doigt de plus en plus près de l'espace entre vos yeux, vous finissez par ne plus le voir.
    • -
    • Le plan éloigné (far plane): Jusqu'à quelle distance de la caméra doit-on afficher les objets.
    • -
    - -

    Nous définissons également la position de la caméra à 5 unités de distance de l'axe Z, qui, comme en CSS, est hors de l'écran vers vous, le spectateur.

    -
  10. -
  11. -

    Le troisième ingrédient essentiel est un moteur de rendu. C'est un objet qui restitue une scène donnée, vu à travers une caméra donnée. Nous allons en créer dès à présent en utilisant le constructeur WebGLRenderer() — mais nous ne l'utiliserons que plus tard. Ajoutez les lignes suivantes à la suite de votre JavaScript:

    - -
    var renderer = new THREE.WebGLRenderer();
    -renderer.setSize(window.innerWidth, window.innerHeight);
    -document.body.appendChild(renderer.domElement);
    - -

    La première ligne crée un nouveau moteur de rendu, la deuxième ligne définit la taille à laquelle le moteur de rendu va dessiner la vue de la caméra, et la troisième ligne ajoute l'élément {{htmlelement("canvas")}} crée par le moteur de rendu au {{htmlelement("body")}} du document. Désormais, tout ce que dessine le moteur de rendu sera affiché dans notre fenêtre.

    -
  12. -
  13. -

    Ensuite, nous voulons créer le cube que nous afficherons sur le canvas. Ajoutez le morceau de code suivant au bas de votre JavaScript:

    - -
    var cube;
    -
    -var loader = new THREE.TextureLoader();
    -
    -loader.load( 'metal003.png', function (texture) {
    -  texture.wrapS = THREE.RepeatWrapping;
    -  texture.wrapT = THREE.RepeatWrapping;
    -  texture.repeat.set(2, 2);
    -
    -  var geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4);
    -  var material = new THREE.MeshLambertMaterial( { map: texture, shading: THREE.FlatShading } );
    -  cube = new THREE.Mesh(geometry, material);
    -  scene.add(cube);
    -
    -  draw();
    -});
    - -

    Il y a un peu plus à encaisser ici, alors allons-ici par étapes:

    - -
      -
    • Nous créons d'abord une variable globale cube pour pouvoir accéder à notre cube de n'importe où dans notre code.
    • -
    • Ensuite, nous créons un nouvel objet TextureLoader, et appellons load() dessus. La fonction load() prend deux paramètres dans notre exemple (bien qu'elle puisse en prendre plus): la texture que nous voulons charger (notre PNG), et la fonction qui sera exécutée lorsque la texture sera chargée.
    • -
    • À l'intérieur de cette fonction, nous utilisons les propriétés de l'objet texture pour spécifier que nous voulons une répétition 2 x 2 de l'image sur tous les côtés du cube.
    • -
    • Ensuite, nous créons un nouvel objet BoxGeometry et MeshLambertMaterial, que nous passons à un Mesh pour créer notre cube. Typiquement, un objet requiert une géométrie (quelle est sa forme) et un matériau (à quoi ressemble sa surface).
    • -
    • Pour finir, nous ajoutons notre cube à la scène, puis appellons la fonction draw() pour commencer l'animation.
    • -
    -
  14. -
  15. -

    Avant de définir la fonction draw(), nous allons ajouter quelques lumières à la scène, pour égayer un peu les choses; ajoutez le bloc suivant à la suite de votre code:

    - -
    var light = new THREE.AmbientLight('rgb(255, 255, 255)'); // soft white light
    -scene.add(light);
    -
    -var spotLight = new THREE.SpotLight('rgb(255, 255, 255)');
    -spotLight.position.set( 100, 1000, 1000 );
    -spotLight.castShadow = true;
    -scene.add(spotLight);
    - -

    Un objet AmbientLight est une lumière douce qui illumine la scène entière, un peu comme le soleil comme vous êtes dehors. Un objet SpotLight, d'autre part, est un faisceau de lumière, plutôt comme une lampe de poche/torche (ou un spotlight - projecteur - en fait).

    -
  16. -
  17. -

    Pour finir, ajoutons notre fonction draw() au bas du code:

    - -
    function draw() {
    -  cube.rotation.x += 0.01;
    -  cube.rotation.y += 0.01;
    -  renderer.render(scene, camera);
    -
    -  requestAnimationFrame(draw);
    -}
    - -

    C'est assez intuittif: sur chaque image, on fait légèrement tourner notre cube sur ses axes X et Y, affichons la scène telle qu'elle vue par notre caméra, puis appellons requestAnimationFrame() pour planifier le dessin de notre prochaine image.

    -
  18. -
- -

Jetons un coup d'oeil rapide au produit fini:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}

- -

Vous pouvez trouver le code terminé sur GitHub.

- -
-

Note : Dans notre repo GitHub vous pouvez également trouver d'autres exemples 3D intéressants — Three.js Video Cube (le voir en direct). On utilise {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} pour prendre un flux vidéo à partir d'une webcam de l'ordinateur et le projetons sur le côté du cube comme texture!

-
- -

Sommaire

- -

À ce stade, vous devriez avoir une idée utile des bases de la programmation graphique en utilisant Canvas et WebGL et de ce que vous pouvez faire avec ces APIs. Vous pouvez trouver des endroits où aller pour trouver plus d'informations dans la section suivante. Amusez-vous!

- -

Voir aussi

- -

Nous n'avons couverts dans cet article que les vraies bases du canvas — il y a tellement plus à apprendre! Les articles suivants vous mèneront plus loin.

- - - -

Exemples

- - - -

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

- -

 

- -

Dans ce module

- - diff --git a/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.md b/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.md new file mode 100644 index 0000000000..95c9f7f8e4 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/drawing_graphics/index.md @@ -0,0 +1,923 @@ +--- +title: Dessiner des éléments graphiques +slug: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics +tags: + - API + - Apprendre + - Articles + - Canvas + - Codage + - Débutant + - Graphismes + - JavaScript + - WebGL +translation_of: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Drawing_graphics +--- +
{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

Le navigateur contient des outils de programmation graphique très puissants, du langage SVG (Scalable Vector Graphics), aux APIs pour dessiner sur les éléments HTML {{htmlelement("canvas")}}, (voir API Canvas et WebGL). Cet article fournit une introduction à canvas et introduit d'autres ressources pour vous permettre d'en apprendre plus.

+ + + + + + + + + + + + +
Prérequis:Bases de JavaScript (voir premiers pas, les briques JavaScript, introduction aux objets), les notions de bases des APIs côté client
Objectif:Apprendre les bases pour dessiner sur des éléments <canvas> en utilisant JavaScript.
+ +

Éléments graphiques sur le Web

+ +

Comme nous en avons parlé dans notre module HTML Multimédia et Intégration, le web était à l'origine uniquement du texte, ce qui était très ennuyeux. Les images ont donc été introduites — d'abord via l'élément {{htmlelement("img")}} et plus tard via les propriétés CSS comme {{cssxref("background-image")}}, et SVG.

+ +

Ce n'était cependant toujours pas assez. Tandis qu'il était possible d'utiliser CSS et JavaScript pour animer (ou manipuler) les images vectorielles SVG — puisqu'elles sont définies par le balisage — il n'y avait aucun moyen de faire de même pour les images bitmap, et les outils disponibles étaient plutôt limités. Le Web n'avait toujours pas de moyen efficace de créer des animations de jeux, des scènes 3D, et autres dispositions couramment traitées par les langages de bas niveau tels que C++ ou Java.

+ +

La situation a commencé à s'améliorer quand les navigateurs ont commencé à prendre en charge l'élément {{htmlelement("canvas")}} et l' API Canvas associée — Apple l'a inventée vers 2004, et les autres navigateurs l'ont l'implémentée dans les années qui ont suivi. Comme vous le verrez dans cet article, canvas fournit de nombreux outils utiles à la création d'animation 2D, jeux, visualisations de données, et autres types d'application, particulièrement quand il est combiné à d'autres APIs que la plateforme web fournit.

+ +

L'exemple ci-dessous montre une simple animation de balles qui rebondissent en canvas 2D, que nous avons déjà vue dans notre module La construction d'objet en pratique:

+ +

{{EmbedGHLiveSample("learning-area/javascript/oojs/bouncing-balls/index-finished.html", '100%', 500)}}

+ +

Autour de 2006-2007, Mozilla a commencé à travailler sur une implémentation expérimentale de canvas 3D. C'est devenu WebGL, lequel a gagné en popularité parmi les fournisseurs de navigateur, et a été standardisé autour de 2009-2010. WebGL permet de créer de véritables graphiques 3D dans le navigateur web; l'exemple ci-dessous montre un simple cube WebGL qui tourne:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}

+ +

Cet article se concentrera principalement sur les canvas 2D, car le code WebGL brut est très complexe. Nous montrerons cependant comment utiliser une bibliothèque WebGL pour créer une scène 3D plus facilement, et vous pourrez  par la suite suivre le tutoriel WebGL, qui couvre le code WebGL brut.

+ +
+

Note : Canvas est très bien pris en charge parmi les différents navigateurs, à l'exception de IE 8 (et inférieur) pour les canvas 2D, et IE 11 (et inférieur) pour WebGL.

+
+ +

Apprentissage actif: Débuter avec un <canvas>

+ +

Si vous voulez créer une scène 2D ou 3D sur une page web, vous devez commencer avec un élément HTML {{htmlelement("canvas")}}. Cet élément est utilisé pour définir la zone de la page où l'image sera dessinée. C'est aussi simple que d'inclure l'élément dans la page:

+ +
<canvas width="320" height="240"></canvas>
+ +

Cela va créer un canvas sur la page d'une taille de 320 pixels par 240.

+ +

À l'intérieur des balises du canvas, vous pouvez mettre du contenu alternatif, qui est affiché si le navigateur de l'utilisateur ne prend pas en charge les canvas.

+ +
<canvas width="320" height="240">
+  <p>Votre navigateur ne prend pas en charge canvas. Boo hoo!</p>
+</canvas>
+ +

Bien sûr, le message ci-dessus est vraiment inutile! Dans un exemple réel, vous voudriez plutôt associer le contenu alternatif au contenu du canvas. Par exemple, si vous voulez afficher un graphique en temps réel des cours boursiers, le contenu alternatif pourrait être une image statique du dernier graphique, avec un texte indiquant quels sont les prix.

+ +

Crée et dimensionner notre canvas

+ +

Commençons par créer notre propre canvas, que nous utiliserons pour dessiner nos futures expériences.

+ +
    +
  1. +

    Premièrement, copiez localement notre fichier 0_canvas_start.html, et ouvez-le dans votre éditeur de texte.

    +
  2. +
  3. +

    Ajoutez le code suivant à l'intérieur, juste après la balise {{htmlelement("body")}} ouvrante:

    + +
    <canvas class="myCanvas">
    +  <p>Ajouter un contenu alternatif approprié ici.</p>
    +</canvas>
    + +

    Nous avons ajouté un attribut class à l'élément <canvas> pour que ce soit plus facile à sélectionner dans le cas où nous aurions plusieurs canvas sur la page. Et nous avons supprimé les attributs width et height pour le moment (vous pouvez les remettre si vous le voulez mais nous les définirons en utilisant JavaScript dans une section plus bas). Les canvas sans hauteur et largeur explicites sont définits par défaut à 300 pixels par 150.

    +
  4. +
  5. +

    Maintenant, ajoutez les lignes suivantes à l'intérieur de l'élément {{htmlelement("script")}}:

    + +
    var canvas = document.querySelector('.myCanvas');
    +var width = canvas.width = window.innerWidth;
    +var height = canvas.height = window.innerHeight;
    + +

    Ici, nous avons stocké une référence vers le canvas dans la variable canvas. Sur la deuxième ligne, nous affectons à la fois une nouvelle variable width et la propriété width du canvas à {{domxref("Window.innerWidth")}} (ce qui nous donne la largeur de la fenêtre). Sur la troisième ligne, nos affectons à la fois une nouvelle variable height et la propriété height du canvas à {{domxref("Window.innerHeight")}} (ce qui nous donne la hauteur de la fenêtre). Nous avons donc un canvas qui remplit toute la largeur et hauteur de la fenêtre!

    + +

    Vous avez également vu que nous avons chaîné les assignations ensemble avec plusieurs signes égal — ce qui est autorié en JavaScript, et c'est une bonne technique si vous voulez que plusieurs variables aient la même valeur. Nous avons gardé la hauteur et largeur du canvas facilement accessibles dans les variables width/height, ces valeurs seront utiles plus tard (par exemple, si vous voulez dessiner quelque chose exactement à mi-chemin de la largeur du canvas).

    +
  6. +
  7. +

    Si vous sauvegardez et chargez votre exemple dans le navigateur maintenant, vous ne verrez rien, ce qui est normal, mais vous verrez également des barres de défilement, ce qui est un problème pour nous. Cela se produit parce que l'élément {{htmlelement("body")}} a des {{cssxref("margin")}} qui, ajoutées à la taille du canvas, résulte en un document qui est plus large que la fenêtre. Pour se débarasser des barres de défilement, nous devons supprimer les {{cssxref("margin")}} et aussi définir {{cssxref("overflow")}} à hidden. Ajoutez ce qui suit à l'intérieur du {{htmlelement("head")}} du document:

    + +
    <style>
    +  body {
    +    margin: 0;
    +    overflow: hidden;
    +  }
    +</style>
    + +

    Les barres de défilement ne devraient plus être là.

    +
  8. +
+ +
+

Note : Vous devrez généralement définir la taille de l'image en utilisant les attributs HTML ou les propriétéss DOM, comme expliqué ci-dessus. Vous pourriez théoriquement utiliser CSS, le problème étant que le dimensionnement le canvas est alors effectué après que le contenu canvas n'ait été calculé, et comme toute autre image (puisque le canvas une fois affiché n'est plus qu'une simple image), elle peut devenir pixelisée/déformée.

+
+ +

Obtenir le contexte du canvas et configuration finale

+ +

Nous devons faire une dernière chose avant de considérer notre template finit. Pour dessiner sur le canvas, nous devons récupérer une référence à la zone de dessin, appelé un contexte. Pour ce faire, on utilise la méthode {{domxref("HTMLCanvasElement.getContext()")}}, qui, pour un usage basique ne prend qu'un seul paramètre, spécifiant quel type de contexte nous voulons récupérer.

+ +

En l'occurence, nous voulons un canvas 2D, alors ajoutez le JavaScript suivant à la suite des autres instructions à l'intérieur de l'élément <script>:

+ +
var ctx = canvas.getContext('2d');
+ +
+

Note : Vous pouvez choisir d'autres types de contexte comme webgl pour WebGL, webgl2 pour WebGL 2, etc., mais nous n'en aurons pas besoin dans cet article.

+
+ +

Voilà — notre canvas est maintenant préparé et prêt à être dessiné! La variable ctx contient désormais un objet {{domxref("CanvasRenderingContext2D")}}, et toutes les opérations de dessin sur le canvas impliqueront de manipuler cet objet.

+ +

Faisons une dernière chose avant de passer à autre chose. Nous allons colorier l'arrière-plan du canvas en noir, cela vous donnera un avant-goût de l'API canvas. Ajoutez les lignes suivantes au bas de votre JavaScript:

+ +
ctx.fillStyle = 'rgb(0, 0, 0)';
+ctx.fillRect(0, 0, width, height);
+ +

Ici nous définissons une couleur de remplissage en utilisant la propriété du canvas {{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle")}} (qui prend une valeur de couleur tout comme les propriétés CSS), puis nous dessinons un rectangle qui recouvre intégralement la surface du canvas avec la méthode {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}} (les deux premiers paramètres sont les coordonnées du coin supérieur gauche du rectangle; les deux derniers sont la largeur et la hauteur du rectangle que vous voulez dessiner — on vous avait dit que ces variables width et height allaient être utiles)!

+ +

OK, notre template est prêt et il est temps de passer à autre chose.

+ +

Les bases du canvas 2D

+ +

Pour rappel, toutes les opération de dessin sont effectuées en manipulant un objet {{domxref("CanvasRenderingContext2D")}} (dans notre cas, ctx).

+ +

De nombreuses opérations doivent recevoir des coordonnées en entrée pour savoir où dessiner quelque chose — le coin supérieur gauche du canvas est le point (0, 0), l'axe horizontal (x) va de gauche à droite, et l'axe vertical (y) va de haut en bas.

+ +

+ +

Dessiner des formes est souvent fait en utilisant la forme rectangle, ou alors en traçant une ligne le long d'un certain chemin puis en remplissant la forme. Nous allons vous montrer ci-dessous comment faire ces deux choses.

+ +

Rectangles simples

+ +

Commençons avec quelques rectangles simples.

+ +
    +
  1. +

    Tout d'abord, faites une copie de votre template (ou copiez localement le fichier 1_canvas_template.html si vous n'avez pas suivit les étapes précédentes).

    +
  2. +
  3. +

    Ensuite, ajoutez les lignes suivantes au bas de votre JavaScript:

    + +
    ctx.fillStyle = 'rgb(255, 0, 0)';
    +ctx.fillRect(50, 50, 100, 150);
    + +

    Si vous sauvegardez votre code et rafraichissez la page, vous devriez voir qu'un rectangle rouge est apparu sur le canvas. Son coin supérieur gauche est à (50,50) pixels du coin supérieur gauche du canvas (comme définit par les deux premiers paramètres), il a une largeur de 100 pixels et une hauteur de 150 pixels (comme définit par les paramètres 3 et 4).

    +
  4. +
  5. +

    Ajoutons un autre rectangle dans le mix — un vert cette fois. Ajoutez ce qui suit au bas de votre JavaScript:

    + +
    ctx.fillStyle = 'rgb(0, 255, 0)';
    +ctx.fillRect(75, 75, 100, 100);
    + +

    Sauvegardez et rafraichissez, et vous verrez un nouveau rectangle. Cela soulève un point important: les opérations graphiques comme dessiner des rectangles, lignes, et autres, sont executées dans l'ordre dans lequel elle apparaissent dans le code. Pensez-y comme peindre un mur, chaque couche de peinture s'ajoute par dessus les anciennes et peuvent même mettre cacher ce qu'il y a en dessous. Vous ne pouvez rien y faire, il faut donc réfléchir soigneusement à l'ordre dans lequel vous allez dessiner les éléments graphiques.

    +
  6. +
  7. +

    Notez que vous pouvez dessiner des éléments graphiques semi-transparents en spécifiant une couleur semi-transparente, par exemple en utilisant rgba(). La valeur a définit ce qu'on appelle le "canal alpha", ou la quantité de transparence de la couleur. Plus la valeur de a est élevée, plus la couleur est opaque. Ajoutez ce qui suit à votre code:

    + +
    ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
    +ctx.fillRect(25, 100, 175, 50);
    +
  8. +
  9. +

    Maintenant essayez de dessiner plus de rectangles par vous-même; amusez-vous!

    +
  10. +
+ +

Traits et épaisseurs de ligne

+ +

Jusqu'à présent nous avons vu comment dessiner des rectangles pleins, mais on peut aussi ne dessiner que les contours (dit strokes - traits - en graphic design). Pour définir la couleur que vous voulez pour le contour, utilisez la propriété {{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle")}}. Pour dessiner le contour du rectangle, on appelle {{domxref("CanvasRenderingContext2D.strokeRect", "strokeRect")}}.

+ +
    +
  1. +

    Ajoutez ce qui suit au bas de votre JavaScript:

    + +
    ctx.strokeStyle = 'rgb(255, 255, 255)';
    +ctx.strokeRect(25, 25, 175, 200);
    +
  2. +
  3. +

    L'épaisseur de trait par défaut est de 1 pixel; vous pouvez ajuster la valeur de la propriété {{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth")}} pour changer ça (prend un nombre spécifiant le nombre de pixels d'épaisseur de trait). Ajoutez la ligne suivante entre les deux lignes précédentes:

    + +
    ctx.lineWidth = 5;
    + +

    Vous devriez maintenant voir que votre contour blanc est devenu beaucoup plus épais!

    +
  4. +
+ +

C'est tout pour le moment. À ce stade votre exemple devrait ressembler à ça:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles.html", '100%', 250)}}

+ +
+

Note : Le code terminé est disponible sur GitHub, 2_canvas_rectangles.html.

+
+ +

Dessiner des chemins

+ +

Si vous voulez dessiner quelque chose de plus complexe qu'un rectangle, vous allez certainement devoir utiliser un path (chemin). En gros, cela implique de spécifier exactement où déplacer un stylo sur le canvas pour tracer la forme que vous voulez. L'API Canvas inclut des fonctions pour dessiner des lignes droites, des cercles, des courbes Bézier, et plus encore.

+ +

Commençons la section en faisant une nouvelle copie de notre template (1_canvas_template.html), dans lequel nous allons dessiner le nouvel exemple.

+ +

Nous allons utiliser quelques méthodes et propriétés communes dans les sections suivantes:

+ + + +

Typiquement, une manière de dessiner un trait simple ressemblerait à ça:

+ +
ctx.fillStyle = 'rgb(255, 0, 0)';
+ctx.beginPath();
+ctx.moveTo(50, 50);
+// tracer le trait
+ctx.fill();
+ +

Dessiner des lignes

+ +

Dessinons un triangle équilatéral sur le canvas.

+ +
    +
  1. +

    Tout d'abord, ajoutez la fonction d'aide suivante au bas de votre code. Elle convertit des valeurs en degrés en radians, ce qui est utile car chaque fois que vous devez fournir une valeur d'angle en JavaScript, ce sera presque toujours en radians, tandis que les humains pensent généralement en degrés.

    + +
    function degToRad(degrees) {
    +  return degrees * Math.PI / 180;
    +};
    +
  2. +
  3. +

    Ensuite, commencez votre path en ajoutant ce qui suit au bas de votre JavaScript. Ici, nous définissons une couleur pour notre triangle et déplaçons le stylo au point (50, 50) sans rien tracer. C'est à partir de là que nous allons dessiner notre triangle.

    + +
    ctx.fillStyle = 'rgb(255, 0, 0)';
    +ctx.beginPath();
    +ctx.moveTo(50, 50);
    +
  4. +
  5. +

    Maintenant ajoutez le bloc de code suivant:

    + +
    ctx.lineTo(150, 50);
    +var triHeight = 50 * Math.tan(degToRad(60));
    +ctx.lineTo(100, 50+triHeight);
    +ctx.lineTo(50, 50);
    +ctx.fill();
    + +

    Parcourons ceci dans l'ordre:

    + +
      +
    1. +

      D'abord nous ajoutons une ligne vers (150, 50) — notre path va maintenant 100 pixels vers la droite le long de l'axe horizontal (x).

      +
    2. +
    3. +

      Puis, nous calculons la hauteur de notre triangle équilatéral, en utilisant un peu de trigonométrie simple. Nous dessinons un triangle pointant vers le bas.

      + +

      Les angles d'un triangle équilatéral sont tous de 60 degrés. Pour calculer la hauteur, nous pouvons séparer le triangle en deux triangles rectangles par le milieu, qui auront alors des angles de 90, 60 et 30 degrés.

      + +

      Pour ce qui est des côtés:

      + +
        +
      • Le côté le plus long est appelé l'hypoténuse
      • +
      • Le côté relié à l'angle de 60 degrés (et qui n'est pas l'hypothénuse) est dit adjacent à cet angle — sa longueur est de 50 pixels puisque c'est la moitié de la ligne que nous avons dessiné.
      • +
      • Le côté opposé à l'angle de 60 degrés est dit opposé à cet angle — c'est la hauteur que nous voulons calculer.
      • +
      + +

       

      + +

      + +

      Une des formule trigonométrique de base stipule que la longueur du côté adjacent mutiplié par la tangente de l'angle est égale à l'opposé, soit 50 * Math.tan(degToRad(60)). Nous utilisons notre fonction degToRad() pour convertir 60 degrés en radians, puisque {{jsxref("Math.tan()")}} attend une valeur en radians.

      +
    4. +
    5. +

      Avec la hauteur calculée, nous ajoutons une nouvelle ligne vers (100, 50+triHeight). La coordonnée X est simple, elle est à mi-chemin de la ligne que nous avons tracé. La valeur de Y d'autre part doit être de 50 plus la hauteur du triangle, puisque le haut du triangle est à 50 pixels du haut du canvas.

      +
    6. +
    7. +

      L'instruction qui suit ajoute une ligne vers le point de départ du triangle.

      +
    8. +
    9. +

      Pour finir, nous appelons ctx.fill() pour finir le path et remplir la forme.

      +
    10. +
    +
  6. +
+ +

Dessiner des cercles

+ +

Maintenant, voyons comment dessiner un cercle sur le canvas. Pour ce faire, on utilise la méthode {{domxref("CanvasRenderingContext2D.arc", "arc()")}}, qui dessine tout ou une portion de cercle à un point spécifié.

+ +
    +
  1. +

    Ajoutons un arc à notre canvas en ajoutant le code qui suit:

    + +
    ctx.fillStyle = 'rgb(0, 0, 255)';
    +ctx.beginPath();
    +ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false);
    +ctx.fill();
    + +

    arc() prend six paramètres.

    + +
      +
    • Les deux premiers spécifient la position du centre du cercle (X et Y respectivement).
    • +
    • Le troisième est le rayon du cercle
    • +
    • Le quatrième et le cinquième sont les angles de début et de fin pour dessiner l'arc (donc spécifier 0 et 360 nous donne un cercle fermé)
    • +
    • Et le sixième paramètre définit si le cercle doit être dessiné dans le sens des aiguilles d'une montre ou dans le sens inverse (false pour le sens horaire).
    • +
    + +
    +

    Note : 0 degrés est horizontalement vers la droite.

    +
    +
  2. +
  3. +

    Ajoutons un autre arc:

    + +
    ctx.fillStyle = 'yellow';
    +ctx.beginPath();
    +ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true);
    +ctx.lineTo(200, 106);
    +ctx.fill();
    + +

    Le motif ici est très similaire, a deux différences près:

    + +
      +
    • Nous avons mis le dernier paramètre de arc() à true, ce qui signifie que l'arc est tracé dans le sens inverse des aiguilles d'une montre.  Donc si notre arc commence à -45 degrés et fini à 45 degrés, nous dessinons un arc de 270 degrés. Si vous changez true à false et ré-exécutez le code, seule une portion de 90 degrés sera dessinée.
    • +
    • Avant d'appeler fill(), nous ajoutons une ligne vers le centre du cercle. Nous obtenons une découpe de style Pac-Man plutôt sympa. Si vous supprimiez cette ligne (essayez!) et ré-exécutiez le code, vous auriez juste un cercle dont le bout a été coupé — entre le début et la fin de l'arc. Cela illuste un autre point important du canvas: si vous essayez de remplir une forme incomplète (qui n'est pas fermée), le navigateur ajoute une ligne droite entre le début et la fin du path et le remplit.
    • +
    +
  4. +
+ +

C'est tout pour le moment; votre exemple final devrait ressembler à ceci:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths.html", '100%', 200)}}

+ +
+

Note : Le code finit est disponible sur GitHub, 3_canvas_paths.html.

+
+ +
+

Note : Pour en savoir plus sur les fonctions de dessin avancées, telles que les courbes Bézier, consultez notre tutoriel Dessiner des formes avec le canevas.

+
+ +

Texte

+ +

Canvas dispose également de fonctionnalités pour écrire du texte. Nous allons les explorer brièvement. Commencez par créer une nouvelle copie du template (1_canvas_template.html), dans lequel nous allons dessiner le nouvel exemple.

+ +

Le texte peut être avec deux méthodes:

+ + + +

Ces deux méthodes prennent trois paramètres: la chaîne de caractères à écrire et les coordonnées X et Y du coin supérieur gauche de la zone de texte (text box) — littéralement, la zone entourant le texte que vous écrivez.

+ +

Il existe également un certain nombre de proprétés pour contrôler le rendu du texte, comme {{domxref("CanvasRenderingContext2D.font", "font")}}, qui permer de spécifier la police d'écriture, la taille, etc — elle accepte la même syntaxe que la propriété CSS {{cssxref("font")}}.

+ +

Essayez d'ajouter le bloc suivant au bas de votre javaScript:

+ +
ctx.strokeStyle = 'white';
+ctx.lineWidth = 1;
+ctx.font = '36px arial';
+ctx.strokeText('Canvas text', 50, 50);
+
+ctx.fillStyle = 'red';
+ctx.font = '48px georgia';
+ctx.fillText('Canvas text', 50, 150);
+ +

Ici nous dessinons deux lignes de texte, une avec le contour et l'autre remplie. L'exemple final devrait ressembler à ça:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text.html", '100%', 180)}}

+ +
+

Note : Le code final est disponible sur GitHub, 4_canvas_text.html.

+
+ +

Jouez avec et voyez ce que vous pouvez faire! Vous pouvez trouver plus d'information sur les options disponibles pour ajouter du texte sur un canvas dans Dessin de texte avec canvas.

+ +

Dessiner des images sur le canvas

+ +

Il est possible d'afficher des images externes sur le canvas. Ce peut être des images simples, des images à l'intérieur d'une vidéo, ou le contenu d'autres canvas. Pour le moment, nous allons juste nous occuper d'ajouter des images simples sur le canvas.

+ +
    +
  1. +

    Comme précédemment, créez une nouvelle copie du template (1_canvas_template.html), où nous dessinerons l'exemple. Vous allez également devoir sauvegarder une copie de notre image d'exemple — firefox.png — dans le même répertoire.

    + +

    Les images sont dessinés sur le canvas en utilisant la méthode {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}}. Dans sa version la plus simple, elle prend trois paramètres — une référence de l'image que vous voulez afficher et les coordonnées X et Y du coin supérieur gauche de l'image sur le canvas.

    +
  2. +
  3. +

    Commençons par obtenir une ressource de l'image à inclure dans notre canvas. Ajoutez les lignes suivantes au bas de votre JavaScript:

    + +
    var image = new Image();
    +image.src = 'firefox.png';
    + +

    Ici, nous créons un nouvel objet {{domxref("HTMLImageElement")}} en utilisant le constructeur {{domxref("HTMLImageElement.Image()", "Image()")}}. (L'objet retourné est le même type que celui retourné lorsque vous récupérez une référence vers un élément {{htmlelement("img")}} existant). Nous définissons son attribut  {{htmlattrxref("src", "img")}} à notre image du logo Firefox. À ce stade, le navigateur commence à charger l'image.

    +
  4. +
  5. +

    Nous pourrions essayer maintenant d'inclure l'image en utilisant drawImage(), mais nous devons nous assurer que le fichier image ait été chargé en premier, faute de quoi le code échouera. Nous pouvons y parvenir en utilisant le gestionnaire d'événement onload, qui ne sera appelé que lorsque l'image aura fini de charger. Ajoutez le bloc suivant à la suite du précédent:

    + +
    image.onload = function() {
    +  ctx.drawImage(image, 50, 50);
    +}
    + +

    Si vous chargez votre exemple dans le navigateur maintenant, vous devriez voir l'image inclue dans le canvas.

    +
  6. +
  7. +

    Mais il y en a plus! Et si nous ne voulions afficher qu'une partie de l'image, ou la redimensionner? Nous pouvons faire ces deux choses avec une version plus complexe de drawImage(). Mettez à jour votre ligne ctx.drawImage() comme suit:

    + +
    ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175);
    + +
      +
    • Le premier paramètre est la référence de l'image, comme précédemment.
    • +
    • Les paramètres 2 et 3 définissent les coordonnées à partir d'où découper l'image, relativement au coin supérieur gauche de l'image d'origine. Tout ce qui est à gauche de X (paramètre 2) ou au-dessus de Y (paramètre 3) ne sera pas dessiné.
    • +
    • Les paramètres 4 et 5 définissent la largeur et hauteur de la zone que nous voulons découper, à partir du coin supérieur gauche de l'image découpée.
    • +
    • Les paramètres 6 et 7 définissent les coordonnées où vous souhaitez placer l'image sur le canvas, relativement au coin supérieur gauche du canvas.
    • +
    • Les paramètres 8 et 9 définissent la largeur et la hauteur affichée de l'image découpée. En l'occurence, nous avons spécifié les mêmes dimensions que la découpe, mais vous pouvez la redimensionner (et la déformer) en spécifiant des valeurs différentes.
    • +
    +
  8. +
+ +

L'exemple final devrait ressembler à ça:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images.html", '100%', 260)}}

+ +
+

Note : Le code final est disponible sur GitHub, 5_canvas_images.html.

+
+ +

Boucles et animations

+ +

Jusqu'ici, nous avons couvert quelques utilisations très basiques du canvas 2D, mais vous ne ressentirez la pleine puissance du canvas que si vous le mettez à jour ou l'animez d'une manière ou d'une autre. Après tout, le canvas fournit des images scriptables! Si vous n'avez pas l'intention de changer quelque chose, alors autant utiiliser des images statiques et vous épargner du travail.

+ +

Créer une boucle

+ +

Jouer avec des boucles est plutôt amusant — vous pouvez exécuter des commandes de canvas à l'intérieur d'une boucle for (ou tout autre type de boucle) comme n'importe quel autre code JavaScript.

+ +

Construisons un exemple simple.

+ +
    +
  1. +

    Créez une nouvelle copie du template (1_canvas_template.html) et ouvrez-le dans votre éditeur de texte.

    +
  2. +
  3. +

    Ajoutez la ligne qui suit au bas de votre JavaScript. Elle contient une nouvelle méthode, {{domxref("CanvasRenderingContext2D.translate", "translate()")}}, qui déplace le point d'origine du canvas:

    + +
    ctx.translate(width/2, height/2);
    + +

    Cela a pour effet de déplacer l'origine des coordonnées (0, 0) au centre du canvas, plutôt que d'être dans le coin supérieur gauche. C'est très utile dans de nombreuses situations, comme celle-ci, où nous voulons que notre dessin soit dessiné par rapport au centre du canvas.

    +
  4. +
  5. +

    Maintenant ajoutez le code suivant au bas du Javacript:

    + +
    function degToRad(degrees) {
    +  return degrees * Math.PI / 180;
    +};
    +
    +function rand(min, max) {
    +  return Math.floor(Math.random() * (max-min+1)) + (min);
    +}
    +
    +var length = 250;
    +var moveOffset = 20;
    +
    +for(var i = 0; i < length; i++) {
    +
    +}
    + +

    Ici, nous implémentons

    + +
      +
    • la même fonction degToRad() que nous avons vu dans l'exemple du triangle auparavant,
    • +
    • une fonction rand(), qui retoune un nombre aléatoire entre une limite inférieure et une limite supérieure,
    • +
    • les variables length et moveOffset (que nous verrons plus loin),
    • +
    • et une boucle for vide.
    • +
    +
  6. +
  7. +

    L'idée est que nous allons dessiner quelque chose sur le canvas à l'intérieur de la boucle for, et itérer dessus pour créer quelque chose d'intéressant. Ajoutez le code suivant à l'intérieur de la boucle for:

    + +
    ctx.fillStyle = 'rgba(' + (255-length) + ', 0, ' + (255-length) + ', 0.9)';
    +ctx.beginPath();
    +ctx.moveTo(moveOffset, moveOffset);
    +ctx.lineTo(moveOffset+length, moveOffset);
    +var triHeight = length/2 * Math.tan(degToRad(60));
    +ctx.lineTo(moveOffset+(length/2), moveOffset+triHeight);
    +ctx.lineTo(moveOffset, moveOffset);
    +ctx.fill();
    +
    +length--;
    +moveOffset += 0.7;
    +ctx.rotate(degToRad(5));
    + +

    Ainsi à chaque itération, on:

    + +
      +
    1. Définit fillStyle comme étant une nuance de violet légèrement transparente, et qui change à chaque fois en fonction de la valeur de length. Comme vous le verrez plus tard, sa valeur diminue à chaque itération, ce qui a pour effet de rendre la couleur toujours plus claire.
    2. +
    3. Ouvre un path.
    4. +
    5. Déplace le stylo aux coordonnées de (moveOffset, moveOffset); Cette variable définit jusqu'où nous voulons nous déplacer à chaque fois que nous dessinons.
    6. +
    7. Dessine une ligne aux coordonées de (moveOffset+length, moveOffset). Cela dessine une ligne de longueur length parallèle à l'axe X.
    8. +
    9. Calcule la hauteur du triangle, comme vu auparavant.
    10. +
    11. Dessine une ligne vers le coin du bas du triangle.
    12. +
    13. Dessine une ligne vers le début du triangle.
    14. +
    15. Appelle fill() pour remplir le triangle.
    16. +
    17. Met à jour les variables qui indiquent comment dessiner le triangle, afin qu'elles soient prêtes pour la prochaine itération: +
        +
      • Diminue la valeur de length de 1, de sorte que les triangles deviennent de plus en plus petits;
      • +
      • Augmente un peu moveOffset pour que chaque triangle successif soit légèrement plus éloigné;
      • +
      • et utilise une nouvelle fonction, {{domxref("CanvasRenderingContext2D.rotate", "rotate()")}}, qui permet de faire pivoter entièrement le canvas! Nous le faisons pivoter de 5 degrés avant de dessiner le triangle suivant.
      • +
      +
    18. +
    +
  8. +
+ +

C'est tout! L'exemple final devrait ressemble à ça:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop.html", '100%', 550)}}

+ +

À ce stade, nous vous encourageons à jouer avec l'exemple et de vous l'approprier! Par exemple:

+ + + +
+

Note : Le code terminé est disponible sur GitHub, 6_canvas_for_loop.html.

+
+ +

Animations

+ +

L'exemple de boucle que nous avons construit ci-dessus était amusant, mais en vrai vous avez besoin d'une boucle qui continue constamment d'itérer pour toute application sérieuse de canvas (telles que les jeux et les visualisations en temps réel). Si vous pensez à votre canvas comme étant en quelque sorte un film, vous allez vouloir que l'affichage se mette à jour à chaque image pour afficher la mise à jour avec un taux de rafraichissement idéal de 60 images par seconde, afin que le mouvement soit lisse et agréable pour l'oeil humain.

+ +

Il y a quelques fonctions JavaScript qui vous permettrons d'exécuter des fonctions de manière répétée, plusieurs fois par seconde, la meilleure étant ici {{domxref("window.requestAnimationFrame()")}}. Elle prend un paramètre — la fonction que vous voulez exécuter pour chaque image. Dès que le navigateur est prêt à mettre à jour l'écran, votre fonction sera appelée. Si cette fonction dessine la nouvelle mise à jour, puis appelle de nouveau requestAnimationFrame() juste avant la fin de la fonction, la boucle d'animation continuera de s'exécuter de manière fluide. La boucle s'arrête lorsque vous vous arrêtez d'appeler requestAnimationFrame() ou si vous appelez {{domxref("window.cancelAnimationFrame()")}} après avoir appelé requestAnimationFrame() mais avant que votre fonction n'ait été exécutée.

+ +
+

Note: C'est une bonne pratique d'appeler cancelAnimationFrame() à partir de votre code principal lorsque vous avez terminé d'utiliser l'animation, pour vous assurer qu'aucune mise à jour n'attend d'être exécutée.

+
+ +

Le navigateur s'occupe des détails complexes tels qu'exécuter l'animation à une vitesse constante, et ne pas gaspiller de ressources en animant des choses qui ne sont pas visibles.

+ +

Pour voir comment cela fonctionne, regardons rapidement notre exemple des balles qui rebondissent (le voir en direct, et voir le code source). Le code de la boucle qui garde le tout en mouvement ressemble à ceci:

+ +
function loop() {
+  ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
+  ctx.fillRect(0, 0, width, height);
+
+  while(balls.length < 25) {
+    var ball = new Ball();
+    balls.push(ball);
+  }
+
+  for(i = 0; i < balls.length; i++) {
+    balls[i].draw();
+    balls[i].update();
+    balls[i].collisionDetect();
+  }
+
+  requestAnimationFrame(loop);
+}
+
+loop();
+ +

Nous lançons la fonction loop() une fois pour commencer le cycle et dessiner la première image de l'animation. La fonction loop() s'occupe ensuite d'appeler requestAnimationFrame(loop) pour afficher la prochaine image de l'animation, et ce continuellement.

+ +

Notez que sur chaque image, nous effaçons complètement le canvas et redessinons tout. Nous créons de nouvelles balles pour chaque image — au maximum 25 — puis, pour chaque balle, la dessinons, mettons à jour sa position, et vérifions si elle est en collision avec une autre balle. Une fois que vous avez dessiné quelque chose sur un canvas, il n'y a aucun moyen pour manipuler cet élément graphique individuellement comme vous pouvez le faire avec les élément DOM. Vous ne pouvez pas déplacer les balles sur le canvas parce qu'une fois dessinée, une balle n'est plus une balle mais juste des pixels sur un canvas. Au lieu de ça, vous devez effacer et redessiner, soit en effaçant et redessinant absolutement tout le canvas, soit en ayant du code qui sait exactement quelles parties doivent être effacées, et qui efface et redessine uniquement la zone minimale nécessaire.

+ +

Optimiser l'animation graphique est une spécialité entière de programmation, avec beaucoup de techniques ingénieuses disponibles. Mais celles-ci sont au-delà de ce dont nous avons besoin pour notre exemple!

+ +

En général, le processus pour animer un canvas implique les étapes suivantes:

+ +
    +
  1. Effacer le contenu du cavas (par exemple avec {{domxref("CanvasRenderingContext2D.fillRect", "fillRect()")}} ou {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}}).
  2. +
  3. Sauvegarder l'état (si nécessaire) en utilisant {{domxref("CanvasRenderingContext2D.save", "save()")}} — c'est nécessaire lorsque vous voulez enregistrer les paramètres que vous mis à jour sur le canvas avant de continuer, ce qui est utile pour des applications plus avancées.
  4. +
  5. Dessiner les éléments graphiques que vous animez.
  6. +
  7. Restaurer les paramètres sauvegardés à l'étape 2 en utilisant {{domxref("CanvasRenderingContext2D.restore", "restore()")}}
  8. +
  9. Appeler requestAnimationFrame() pour planifier le dessin de l'image suivante de l'animation.
  10. +
+ +
+

Note : Nous ne couvrirons pas save() et restore() ici, mais elles sont bien expliquées dans notre tutoriel Transformations (et ceux qui le suivent).

+
+ +

Une animation simple de personnage

+ +

Créons maintenant notre propre animation simple — nous allons faire parcourir l'écran à un personnage d'un certain jeu vidéo rétro plutôt génial.

+ +
    +
  1. +

    Faites une nouvelle copie du template (1_canvas_template.html) et ouvrez-le dans votre éditeur de texte. Sauvegardez une copie de walk-right.png dans le même répertoire.

    +
  2. +
  3. +

    Au bas du JavaScript, ajoutez la ligne suivante pour placer une fois de plus l'origine des coordonnées au milieu du canvas:

    + +
    ctx.translate(width/2, height/2);
    +
  4. +
  5. +

    Nous allons maintenant créer un objet {{domxref("HTMLImageElement")}}, définir son attribut {{htmlattrxref("src", "img")}} à l'image que nous voulons charger, et ajouter le gestionnaire d'événement onload pour appeler la fonction draw() quand l'image sera chargée:

    + +
    var image = new Image();
    +image.src = 'walk-right.png';
    +image.onload = draw;
    +
  6. +
  7. +

    Ajoutons quelques variables pour garder une trace de la position du sprite à dessiner à l'écran, et le numéro du sprite que nous voulons afficher.

    + +
    var sprite = 0;
    +var posX = 0;
    + +

    L'image de sprites (que nous avons respectueusement emprunté à Mike Thomas dans son article Create a sprite sheet walk cycle using using CSS animation — créer un cycle de marche avec une feuille de sprites en utilisant les animations CSS) ressemble à ça:

    + +

    + +

    Elle contient six sprites qui constituent une séquence de marche — chacun a 102 pixels de large et 148 pixels de hauteur. Pour afficher chaque sprite proprement, nous allons devoir utiliser drawImage() pour découper un seul sprite de l'image et n'afficher que cette partie, comme nous l'avons fait précédemment avec le logo Firefox. La coordonnée X de la découpe devra être un multiple de 102 et la coordonnée Y sera toujours 0. La taille de la découpe sera toujours de 102 pixels par 148.

    +
  8. +
  9. +

    Insérons maintenant une fonction draw() vide au bas du code, prête à être remplie de code:

    + +
    function draw() {
    +
    +};
    +
  10. +
  11. +

    Le reste du code dans cette section va dans draw(). Tout d'abord, ajoutez la ligne suivante, qui efface le canvas pour préparer le dessin de chaque image. Notez que nous devons spécifier le coin supérieur gauche du rectangle comme étant -(width/2), -(height/2) puisque nous avons définit l'origine du canvas à width/2, height/2 plus tôt.

    + +
    ctx.fillRect(-(width/2), -(height/2), width, height);
    +
  12. +
  13. +

    Ensuite, nous allons dessinons notre image en utilisant drawImage() — la version à 9 paramètres. Ajoutez ce qui suit:

    + +
    ctx.drawImage(image, (sprite*102), 0, 102, 148, 0+posX, -74, 102, 148);
    + +

    Comme vous pouvez le voir:

    + +
      +
    • Nous spécifions image comme étant l'image à inclure.
    • +
    • Les paramètres 2 et 3 spécifient le coin supérieur gauche de la portion de l'image à découper: la valeur X vaut sprite multiplié par 102 (où sprite est le numéro du sprite, entre 0 et 5) et la valeur Y vaut toujours 0.
    • +
    • Les paramètres 4 et 5 spécifient la taille de la découpe — 102 pixels par 148.
    • +
    • Les paramètres 6 et 7 spécifient le coin supérieur gauche de la découpe sur le canvas — la position de X est 0+posX, ce qui signifie que nous pouvons modifier la position du dessin en modifiant la valeur de posX.
    • +
    • Les paramètres 8 et 9 spécifient la taille de l'image sur le canvas. Nous voulons garder sa taille d'origine, on définit donc 102 pour largeur et 148 pour hauteur.
    • +
    +
  14. +
  15. +

    Maintenant, nous allons changer la valeur de sprite après chaque dessin — après certains d'entre eux du moins. Ajoutez le bloc de code suivant au bas de la fonction draw():

    + +
      if (posX % 13 === 0) {
    +    if (sprite === 5) {
    +      sprite = 0;
    +    } else {
    +      sprite++;
    +    }
    +  }
    + +

    Nous enveloppons le bloc entier de if (posX % 13 === 0) { ... }. On utilise l'opérateur modulo (%) (aussi connu sous le nom d'opérateur reste) pour vérifier si la valeur de posX peut être exactement divisée par 13, sans reste. Si c'est le cas, on passe au sprite suivant en incrémentant sprite (en retournant à 0 après le sprite #5). Cela implique que nous ne mettons à jour le sprite que toutes les 13èmes images, ou à peu près 5 images par seconde (requestAnimationFrame() appelle jusqu'à 60 images par secondes si possible). Nous ralentissons délibéremment le cadence des images parce que nous n'avons que six sprites avec lesquels travailler, et si on en affiche un à chaque 60ième de seconde, notre personnage va bouger beaucoup trop vite!

    + +

    À l'intérieur du bloc, on utilise une instruction if ... else pour vérifier si la valeur de sprite vaut 5 (le dernier sprite, puisque les numéro de sprite vont de 0 à 5). Si nous sommes en train d'afficher le dernier sprite, alors on réinitialilse sprite à 0; sinon on l'incrémente de 1.

    +
  16. +
  17. +

    Ensuite, nous devons déterminer comment modifier la valeur de posX sur chaque image — ajoutez le bloc de code à la suite:

    + +
      if(posX > width/2) {
    +    newStartPos = -((width/2) + 102);
    +    posX = Math.ceil(newStartPos / 13) * 13;
    +    console.log(posX);
    +  } else {
    +    posX += 2;
    +  }
    + +

    Nous utilisons une autre instruction if ... else pour voir si la valeur de posX est plus grande que width/2, ce qui signifie que notre personnage est sortit du bord droit de l'écran. Si c'est le cas, on calcule la position qui met le personnage à gauche du bord gauche de l'écran, et on définit posX au multiple de 13 le plus proche de ce nombre. Il faut obligatoirement un multiple de 13 pour que le bloc de code précédent puisse toujours fonctionner!

    + +

    Si notre personnage n'a pas atteint le bord de l'écran, on incrémente posX de 2. Cela le fera bouger un peu vers la droite la prochaine fois qu'on le dessinera.

    +
  18. +
  19. +

    Finalement, nous devons boucler sur l'animation en appelannt {{domxref("window.requestAnimationFrame", "requestAnimationFrame()")}} en bas de la fonction draw():

    + +
    window.requestAnimationFrame(draw);
    +
  20. +
+ +

Et voilà! L'exemple final devrait ressembler à ça:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}

+ +
+

Note : Le code final est disponible sur GitHub, 7_canvas_walking_animation.html.

+
+ +

Une application simple de dessin

+ +

Comme exemple final d'animation, nous aimerions vous montrer une application très simple de dessin, pour illustrer comment la boucle d'animation peut être combinée avec les entrées de l'utilisateur (comme le mouvement de la souris en l'occurence). Nous n'allons pas expliquer la procédure pas à pas pour construire cette application, nous allons juste explorer les parties les plus intéressantes du code.

+ +

L'exemple peut être trouvé sur GitHub, 8_canvas_drawing_app.html, et vous pouvez jouer avec en direct ci-dessous:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app.html", '100%', 600)}}

+ +

Regardons les parties les plus intéressantes. Tout d'abord, nous gardons une trace des coordonnées X et Y de la souris et si elle est pressée ou non grâce à trois variables: curX, curY, et pressed. Quand la souris bouge, nous déclenchons une fonction via le gestionnaire d'événement onmousemove, lequel capture les valeurs X et Y actuelles. Nous utilisons également les gestionnaires d'événement onmousedown et onmouseup pour changer la valeur de pressed à true quand le bouton de la souris est pressé, et de nouveau à false quand il est relaché.

+ +
var curX;
+var curY;
+var pressed = false;
+
+document.onmousemove = function(e) {
+  curX = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
+  curY = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
+}
+
+canvas.onmousedown = function() {
+  pressed = true;
+};
+
+canvas.onmouseup = function() {
+  pressed = false;
+}
+ +

Quand le bouton "Clear canvas" (effacer le canvas) est cliqué, nous exécutons une simple fonction qui efface entièrement le canvas grâce à un rectangle noir, de la même manière que nous avons vu précédemment:

+ +
clearBtn.onclick = function() {
+  ctx.fillStyle = 'rgb(0, 0, 0)';
+  ctx.fillRect(0, 0, width, height);
+}
+ +

La boucle du dessin est relativement simple cette fois-ci — si pressed est à true, nous dessinons un cercle rempli avec la couleur du color picker (sélecteur de couleur), et d'un rayon égal à la valeur définit dans le champs de sélection dans un intervalle.

+ +
function draw() {
+  if(pressed) {
+    ctx.fillStyle = colorPicker.value;
+    ctx.beginPath();
+    ctx.arc(curX, curY-85, sizePicker.value, degToRad(0), degToRad(360), false);
+    ctx.fill();
+  }
+
+  requestAnimationFrame(draw);
+}
+
+draw();
+ +
+

Note : Les types d'{{htmlelement("input")}} range et color sont relativement bien pris en charge parmi les différents navigateurs, à l'exception des versions d'Internet Explorer inférieures à 10; et Safari ne prend pas en charge le type color. Si votre navigateur ne prend pas en charge ces types, il affichera simplement un champ texte et vous n'aurez qu'à entrer des valeurs de couleur et numéro valides vous-mêmes.

+
+ +

WebGL

+ +

Il est maintenant temps de laisser la 2D derrière, et de jeter un coup d'oeil au canvas 3D. Le contenu 3D d'un canvas est spécifié en utilisant l'API WebGL, qui est une API complètement séparée de l'API des canvas 2D, même si ls deux sont affichés sur des éléments {{htmlelement("canvas")}}.

+ +

WebGL est basé sur le langage de programmation graphique OpenGL, et permet de communiquer directement avec le GPU de l'ordinateur. En soi, l'écriture WebGL est plus proche des langages de bas niveau tel que C++ que du JavaScript usuel; c'est assez complexe mais incroyablement puissant.

+ +

Utiliser une bibliothèque

+ +

De par sa complexité, la plupart des gens écrivent du code de graphique 3D en utilisant une bibliothèque JavaScript tierce telle que Three.js, PlayCanvas ou Babylon.js. La plupart d'entre elles fonctionnent d'une manière similaire, offrant des fonctionnalités pour créer des formes primitives et personnalisées, positionner des caméras et des éclairages, recouvrir des surfaces avec des textures et plus encore. Elles se chargent de WebGL pour vous, vous permettant de travailler à un niveau plus haut.

+ +

Oui, en utiliser une signifie apprendre une autre nouvelle API (une API tierce en l'occurence), mais elles sont beaucoup plus simples que de coder du WebGL brut.

+ +

Recréer notre cube

+ +

Regardons un exemple simple pour créer quelque chose avec une bibliothèque WebGL. Nous allons choisir Three.js, puisque c'est l'une des plus populaires. Dans ce tutoriel, nous allons créer le cube 3D qui tourne que nous avons plus tôt.

+ +
    +
  1. +

    Pour commencer, créez une copie locale de index.html dans un nouveau répertoire, et sauvegardez metal003.png dans ce même répertoire. C'est l'image que nous allons utiliser comme texture de surface du cube plus tard.

    +
  2. +
  3. +

    Ensuite, créez un nouveau fichier main.js, toujours dans le même répertoire.

    +
  4. +
  5. +

    Si vous ouvrez index.html dans votre éditeur de texte, vous verrez qu'il y a deux éléments {{htmlelement("script")}} — le premier ajoute three.min.js à la page, et le second ajoute notre fichier main.js à la page. Vous devez télécharger la bibliothèque three.min.js et la sauvegarder dans le même répertoire que précédemment.

    +
  6. +
  7. +

    Maintenant que nous avons inclus three.js dans notre page, nous pouvons commencer à écrire du code JavaScript qui l'utilise dans main.js. Commençons par créer une nouvelle scène — ajoutez ce qui suit dans le fichier main.js:

    + +
    var scene = new THREE.Scene();
    + +

    Le constructeur Scene() crée une nouvelle scène, qui représente l'ensemble du monde 3D que nous essayons d'afficher.

    +
  8. +
  9. +

    Ensuite, nous avons besoin d'une caméra pour voir la scène. En terme d'imagerie 3D, la caméra représente la position du spectateur dans le monde. Pour ajouter une caméra, ajoutez les lignes suivantes à la suite:

    + +
    var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    +camera.position.z = 5;
    +
    + +

    Le constructeur PerspectiveCamera() prend quatre arguments:

    + +
      +
    • Le champ de vision: Quelle est la largeur de la zone devant la caméra qui doit être visible à l'écran, en degrés.
    • +
    • Le rapport d'aspect (aspect ratio): Habituellement, c'est le rapport entre la largeur de la scène divisé par la hauteur de la scène. Utiliser une autre valeur va déformer la scène (ce qui pourrait être ce que vous voulez, mais ce n'est généralement pas le cas).
    • +
    • Le plan proche (near plane): Jusqu'où les objets peuvent être proches de la caméra avant que nous arrêtions de les afficher à l'écran. Pensez-y comme quand vous approchez votre doigt de plus en plus près de l'espace entre vos yeux, vous finissez par ne plus le voir.
    • +
    • Le plan éloigné (far plane): Jusqu'à quelle distance de la caméra doit-on afficher les objets.
    • +
    + +

    Nous définissons également la position de la caméra à 5 unités de distance de l'axe Z, qui, comme en CSS, est hors de l'écran vers vous, le spectateur.

    +
  10. +
  11. +

    Le troisième ingrédient essentiel est un moteur de rendu. C'est un objet qui restitue une scène donnée, vu à travers une caméra donnée. Nous allons en créer dès à présent en utilisant le constructeur WebGLRenderer() — mais nous ne l'utiliserons que plus tard. Ajoutez les lignes suivantes à la suite de votre JavaScript:

    + +
    var renderer = new THREE.WebGLRenderer();
    +renderer.setSize(window.innerWidth, window.innerHeight);
    +document.body.appendChild(renderer.domElement);
    + +

    La première ligne crée un nouveau moteur de rendu, la deuxième ligne définit la taille à laquelle le moteur de rendu va dessiner la vue de la caméra, et la troisième ligne ajoute l'élément {{htmlelement("canvas")}} crée par le moteur de rendu au {{htmlelement("body")}} du document. Désormais, tout ce que dessine le moteur de rendu sera affiché dans notre fenêtre.

    +
  12. +
  13. +

    Ensuite, nous voulons créer le cube que nous afficherons sur le canvas. Ajoutez le morceau de code suivant au bas de votre JavaScript:

    + +
    var cube;
    +
    +var loader = new THREE.TextureLoader();
    +
    +loader.load( 'metal003.png', function (texture) {
    +  texture.wrapS = THREE.RepeatWrapping;
    +  texture.wrapT = THREE.RepeatWrapping;
    +  texture.repeat.set(2, 2);
    +
    +  var geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4);
    +  var material = new THREE.MeshLambertMaterial( { map: texture, shading: THREE.FlatShading } );
    +  cube = new THREE.Mesh(geometry, material);
    +  scene.add(cube);
    +
    +  draw();
    +});
    + +

    Il y a un peu plus à encaisser ici, alors allons-ici par étapes:

    + +
      +
    • Nous créons d'abord une variable globale cube pour pouvoir accéder à notre cube de n'importe où dans notre code.
    • +
    • Ensuite, nous créons un nouvel objet TextureLoader, et appellons load() dessus. La fonction load() prend deux paramètres dans notre exemple (bien qu'elle puisse en prendre plus): la texture que nous voulons charger (notre PNG), et la fonction qui sera exécutée lorsque la texture sera chargée.
    • +
    • À l'intérieur de cette fonction, nous utilisons les propriétés de l'objet texture pour spécifier que nous voulons une répétition 2 x 2 de l'image sur tous les côtés du cube.
    • +
    • Ensuite, nous créons un nouvel objet BoxGeometry et MeshLambertMaterial, que nous passons à un Mesh pour créer notre cube. Typiquement, un objet requiert une géométrie (quelle est sa forme) et un matériau (à quoi ressemble sa surface).
    • +
    • Pour finir, nous ajoutons notre cube à la scène, puis appellons la fonction draw() pour commencer l'animation.
    • +
    +
  14. +
  15. +

    Avant de définir la fonction draw(), nous allons ajouter quelques lumières à la scène, pour égayer un peu les choses; ajoutez le bloc suivant à la suite de votre code:

    + +
    var light = new THREE.AmbientLight('rgb(255, 255, 255)'); // soft white light
    +scene.add(light);
    +
    +var spotLight = new THREE.SpotLight('rgb(255, 255, 255)');
    +spotLight.position.set( 100, 1000, 1000 );
    +spotLight.castShadow = true;
    +scene.add(spotLight);
    + +

    Un objet AmbientLight est une lumière douce qui illumine la scène entière, un peu comme le soleil comme vous êtes dehors. Un objet SpotLight, d'autre part, est un faisceau de lumière, plutôt comme une lampe de poche/torche (ou un spotlight - projecteur - en fait).

    +
  16. +
  17. +

    Pour finir, ajoutons notre fonction draw() au bas du code:

    + +
    function draw() {
    +  cube.rotation.x += 0.01;
    +  cube.rotation.y += 0.01;
    +  renderer.render(scene, camera);
    +
    +  requestAnimationFrame(draw);
    +}
    + +

    C'est assez intuittif: sur chaque image, on fait légèrement tourner notre cube sur ses axes X et Y, affichons la scène telle qu'elle vue par notre caméra, puis appellons requestAnimationFrame() pour planifier le dessin de notre prochaine image.

    +
  18. +
+ +

Jetons un coup d'oeil rapide au produit fini:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}

+ +

Vous pouvez trouver le code terminé sur GitHub.

+ +
+

Note : Dans notre repo GitHub vous pouvez également trouver d'autres exemples 3D intéressants — Three.js Video Cube (le voir en direct). On utilise {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} pour prendre un flux vidéo à partir d'une webcam de l'ordinateur et le projetons sur le côté du cube comme texture!

+
+ +

Sommaire

+ +

À ce stade, vous devriez avoir une idée utile des bases de la programmation graphique en utilisant Canvas et WebGL et de ce que vous pouvez faire avec ces APIs. Vous pouvez trouver des endroits où aller pour trouver plus d'informations dans la section suivante. Amusez-vous!

+ +

Voir aussi

+ +

Nous n'avons couverts dans cet article que les vraies bases du canvas — il y a tellement plus à apprendre! Les articles suivants vous mèneront plus loin.

+ + + +

Exemples

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

+ +

 

+ +

Dans ce module

+ + diff --git a/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.html b/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.html deleted file mode 100644 index 9fe8ea07d5..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.html +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: Récupérer des données du serveur -slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data -tags: - - API - - Apprendre - - Article - - Codage - - Débutant - - Fetch - - JavaScript - - XHR - - data -translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Fetching_data ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
- -

Une autre tâche courante dans les sites et applications web modernes est de récupérer des données à partir du serveur pour mettre à jour des sections de la page web sans la recharger entièrement. Ce qui pourrait paraître comme un petit détail a, en vérité, eu un impact énorme sur les performances et le comportement des sites. Dans cet article, nous allons expliquer le concept et les technologies qui rendent cela possible, tels que XMLHttpRequest et l'API Fetch.

- - - - - - - - - - - - -
Prérequis :Notions de base de JavaScript (voir premiers pas, les briques JavaScript, les objets JavaScript), les notions de bases des APIs côté client
Objectif :Apprendre à récupérer des données du serveur web et les utiliser pour mettre à jour le contenu de la page.
- -

Quel est le problème?

- -

À la base, le chargement d'une page web est simple — vous envoyez une requête à un serveur et, tant qu'il n'y a pas eu de problème, les ressources de la page web sont téléchargées et affichées sur votre ordinateur.

- -

A basic representation of a web site architecture

- -

Le problème avec ce modèle c'est qu'à chaque fois que vous voulez mettre à jour une partie de la page, par exemple pour afficher la page suivante d'une liste de produits, vous devez recharger toute la page. Ce surcoût est tout à fait inutile et résulte en une mauvaise expérience utilisateur, particulièrement pour les pages qui sont lourdes, complexes et du coup longues à charger.

- -

L'arrivée d'Ajax

- -

Pour traiter ce problème, des technologies ont été élaborées qui permettent de récupérer à la demande de petites portions de données (comme du HTML, {{glossary("XML")}}, JSON, ou texte brut) et de les afficher dans la page web.

- -

Nous avons pour cela l'API {{domxref("XMLHttpRequest")}} à notre disposition ou — plus récemment — l'API Fetch. Elles permettent de réaliser des requêtes HTTP pour récupérer des ressources spécifiques disponibles sur un serveur et de formater les données retournées selon les besoins avant l'affichage dans la page.

- -
-

Note : Dans les premiers temps, cette technique était appelée "Asynchronous JavaScript and XML" (JavaScript asychrone et XML), dit AJAX, parce qu'elle utilisait {{domxref("XMLHttpRequest")}} pour requêter des données XML. De nos jours, c'est rarement le cas; la plupart du temps, on utilise XMLHttpRequest ou Fetch pour requêter des données JSON. Quoi qu'il en soit, le procédé reste le même et le terme "Ajax" est resté pour décrire cette technique.

-
- -

A simple modern architecture for web sites

- -

Le modèle Ajax implique une API web comme proxy pour requêter les données plus intelligemment que simplement rafraîchir la page à chaque fois. Voyons plutôt l'impact que cela a :

- -
    -
  1. Allez sur un site riche en information de votre choix, comme Amazon, YouTube, CNN...
  2. -
  3. Cherchez quelque chose dans la barre de recherche, comme un nouveau produit. Le contenu principal va changer, mais la plupart de ce qui l'entoure reste statique, comme l'entête, le pied de page, le menu de navigation, etc.
  4. -
- -

C'est une bonne chose puisque :

- - - -

Notez que pour accélerer les choses encore davantage, certains sites stockent les ressources et données chez le client lors de sa première visite, si bien que les visites suivantes, les fichiers locaux sont utilisés et non re-téléchargés du serveur. Le contenu n'est rechargé que lorsqu'il a été mis à jour sur le serveur.

- -

A basic web app data flow architecture

- -

Une requête Ajax basique

- -

Voyons maintenant comment ces requêtes sont gérées, en utilisant soit {{domxref("XMLHttpRequest")}} soit Fetch. Pour ces exemples, nous allons requêter les données de différents fichiers texte et les utiliserons pour remplir une zone de contenu.

- -

Ces fichiers agiront comme une fausse base de données ; dans une vraie application, il est plus probable que vous utiliseriez un langage côté serveur comme PHP, Python, ou Node pour récupérer les données à partir d'une véritable base de données. En revanche, nous voulons ici garder les choses simples ; nous allons donc nous concentrer sur le côté client.

- -

XMLHttpRequest

- -

XMLHttpRequest (qui est fréquemment abrégé XHR) est une technologie assez vieille maintenant — elle a été inventée par Microsoft dans les années 90 et a été standardisée dans les autres navigateurs il y a longtemps.

- -
    -
  1. -

    Pour commencer cet exemple, faites une copie locale de ajax-start.html et des quatre fichiers texte — verse1.txt, verse2.txt, verse3.txt, et verse4.txt — dans un nouveau répertoire sur votre ordinateur. Dans cet exemple, nous allons charger le verset d'un poème (que vous pourriez bien reconnaître), quand il est sélectionné dans le menu déroulant, en utilisant XHR.

    -
  2. -
  3. -

    À l'intérieur de l'élément {{htmlelement("script")}}, ajoutez le code qui suit. Il stocke une référence aux éléments {{htmlelement("select")}} et {{htmlelement("pre")}} dans des variables et définit un gestionnaire d'événement {{domxref("GlobalEventHandlers.onchange","onchange")}}, pour que, quand la valeur du menu déroulant est changée, la valeur sélectionnée soit passée comme paramètre à la fonction  updateDisplay().

    - -
    var verseChoose = document.querySelector('select');
    -var poemDisplay = document.querySelector('pre');
    -
    -verseChoose.onchange = function() {
    -  var verse = verseChoose.value;
    -  updateDisplay(verse);
    -};
    -
  4. -
  5. -

    Définissons maintenant la fonction updateDisplay(). Tout d'abord, mettez ce qui suit au bas de votre JavaScript — c'est la structure vide de la fonction :

    - -
    function updateDisplay(verse) {
    -
    -};
    -
  6. -
  7. -

    Nous allons commencer notre fonction en construisant une URL relative qui pointe vers le fichier texte que nous voulons charger, nous en aurons besoin plus tard. La valeur de l'élément {{htmlelement("select")}} à tout instant est la même que l'élément {{htmlelement("option")}} sélectionné (c'est à dire le texte de l'élément sélectionné, ou son attribut value s'il est spécifié) — par exemple "Verse 1". Le fichier correspondant est "verse1.txt" et il est situé dans le même répertoire que le fichier HTML, le nom du fichier seul suffira donc.

    - -

    Les serveurs web sont généralement sensibles à la casse, le nom de fichier n'a pas d'espace et a une extension de fichier. Pour convertir "Verse 1" en "verse1.txt" nous allons convertir le "V" en minuscles avec {{jsxref("String.toLowerCase", "toLowerCase()")}}, supprimer l'espace avec {{jsxref("String.replace", "replace()")}} et ajouter ".txt" à la fin avec une simple concaténation de chaînes. Ajoutez les lignes suivantes à l'intérieur de la fonction updateDisplay() :

    - -
    verse = verse.replace(" ", "");
    -verse = verse.toLowerCase();
    -var url = verse + '.txt';
    -
  8. -
  9. -

    Pour commencer à créer une requête XHR, vous allez devoir créer un nouvel objet avec le constructeur {{domxref("XMLHttpRequest.XMLHttpRequest", "XMLHttpRequest()")}}. Vous pouvez appeler cet objet comme vous le voulez, mais nous l'appellerons request pour plus de clarté. Ajoutez ce qui suit à vos lignes précédentes :

    - -
    var request = new XMLHttpRequest();
    -
  10. -
  11. -

    Ensuite, vous allez devoir utiliser la méthode {{domxref("XMLHttpRequest.open","open()")}} pour spécifier la méthode HTTP et l'URL à utiliser pour récupérer la ressource. Nous allons ici utiliser la méthode GET et passer notre variable url pour URL. Ajoutez ceci à la suite de la ligne précédente :

    - -
    request.open('GET', url);
    -
  12. -
  13. -

    Nous allons définir le type de réponse que nous attendons — définit par la propriété {{domxref("XMLHttpRequest.responseType", "responseType")}} de la requête — comme text. Ce n'est pas strictement nécessaire ici — XHR retourne du texte par défaut — mais c'est une bonne idée d'en prendre l'habitude pour les cas où vous aurez besoin de définir un type différent. Ajoutez ceci à la suite :

    - -
    request.responseType = 'text';
    -
  14. -
  15. -

    Récupérer une ressource sur le réseau est une opération {{glossary("asynchronous","asynchrone")}}, ce qui signifie que vous devez attendre que cette opération se termine (par exemple, que la ressource soit renvoyée) avant de pouvoir récupérer la réponse — sans quoi une erreur est levée. XHR permet d'exécuter du code lorsque la réponse est reçue grâce au gestionnaire d'événement {{domxref("XMLHttpRequest.onload", "onload")}} — quand l'événement {{event("load")}} est déclenché. Une fois que la réponse a été reçue, alors la réponse est accessible via la propriété response de l'objet XHR utilisé.

    - -

    Ajoutez le bloc de code qui suit toujours au bas de la fonction updateDisplay(). Vous verrez qu'à l'intérieur du gestionnaire d'événément onload, nous assignons la propriété textContent de poemDisplay (l'élément {{htmlelement("pre")}}) à la valeur de la propriété {{domxref("XMLHttpRequest.response", "request.response")}}.

    - -
    request.onload = function() {
    -  poemDisplay.textContent = request.response;
    -};
    -
  16. -
  17. -

    Les étapes précédentes nous ont permis de configurer la requête XHR, mais celle-ci n'est exécutée que lorsqu'on le demande explicitement. Pour ce faire, il faut appeler la méthode {{domxref("XMLHttpRequest.send","send()")}}. Ajoutez la ligne suivante à la suite du code déjà écrit :

    - -
    request.send();
    - -

    Voyez la section {{anch("Serving your example from a server", "Servir votre exemple depuis un serveur")}} pour pouvoir tester.

    -
  18. -
  19. -

    Un dernier problème avec cet exemple est qu'il ne montre rien au chargement de la page (mais uniquement à la sélection d'un verset). Pour corriger cela, ajoutez ce qui suit au bas de votre code (juste au-dessus de la balise fermante </script>), pour charger le verset 1 par défaut, et s'assurer que l'élément {{htmlelement("select")}} montre toujours la bonne valeur :

    - -
    updateDisplay('Verse 1');
    -verseChoose.value = 'Verse 1';
    -
  20. -
- -

Servir votre exemple depuis un serveur

- -

Certains navigateurs (dont Chrome) n'exécuteront pas de requêtes XHR si vous exécutez votre script simplement à partir d'un fichier local. Cela est dû à des restrictions de sécurité (pour plus d'infos sur la sécurité web, consultez l'article La sécurité d'un site web).

- -

Pour règler ce problème, vous devez tester l'exemple à travers un serveur web local. Pour savoir comment procéder, lisez Comment configurer un serveur de test local?

- -

Fetch

- -

L'API Fetch est une solution moderne qui vient remplacer XHR — elle a été introduite récemment dans les navigateurs pour rendre les requêtes HTTP asynchrones plus simples en JavaScript, à la fois pour les développeurs et pour les autres APIs qui utilisent cette technologie.

- -

Voyons comment convertir le dernier exemple, en remplaçant XHR par Fetch.

- -
    -
  1. -

    Faites une copie du répertoire de votre dernier exemple. (Ou si vous ne l'avez pas fait, créez un nouveau répertoire et copiez le fichier xhr-basic.html et les quatre fichiers texte — verse1.txt, verse2.txt, verse3.txt, and verse4.txt — à l'intérieur).

    -
  2. -
  3. -

    À l'intérieur de la fonction updateDisplay(), vous avez le code XHR suivant :

    - -
    var request = new XMLHttpRequest();
    -request.open('GET', url);
    -request.responseType = 'text';
    -
    -request.onload = function() {
    -  poemDisplay.textContent = request.response;
    -};
    -
    -request.send();
    -
  4. -
  5. -

    Remplacez-le avec ce qui suit :

    - -
    fetch(url).then(function(response) {
    -  response.text().then(function(text) {
    -    poemDisplay.textContent = text;
    -  });
    -});
    -
  6. -
  7. -

    Chargez l'exemple dans votre navigateur (en l'exécutant à travers un serveur web) et il devrait produire le même résultat que la version XHR  — pourvu que vous utilisiez un navigateur moderne.

    -
  8. -
- -

Que se passe-t-il dans le code Fetch?

- -

Tout d'abord, nous invoquons la méthode {{domxref("WindowOrWorkerGlobalScope.fetch()","fetch()")}}, en lui passant l'URL de la ressource que nous voulons récupérer. C'est la version moderne équivalente à {{domxref("XMLHttpRequest.open","request.open()")}} de XHR, et n'avez pas à appeler .send() — la requête est exécutée directemment.

- -

Ensuite, la méthode {{jsxref("Promise.then",".then()")}} est chaînée à la suite de fetch() — cette méthode fait partie des {{jsxref("Promise","Promesses")}}, une fonctionnalité JavaScript moderne qui permet d'effectuer des opérations asynchrones. fetch() retourne une promesse, qui est résolue lorsque la réponse est reçue du serveur — et nous utilisons .then() pour exécuter du code à ce moment là. C'est l'équivalent du gestionnaire d'événément onload dans la version XHR.

- -

La fonction définie dans le .then() reçoit la réponse du serveur comme paramètre, une fois que la promesse retournée par fetch() est résolue. À l'intérieur de cette fonction, nous utilisons la méthode {{domxref("Body.text","text()")}} pour récupérer le contenu de la réponse en texte brut. C'est l'équivalent de request.responseType = 'text' dans la version XHR.

- -

Vous verrez que text() retourne également une promesse, nous y chaînons donc un nouveau .then(), à l'intérieur de quoi nous définissons une fonction. Cette dernière récupère quant à elle le texte brut que la promesse précédente résout.

- -

Enfin, dans le corps de la fonction, nous faisons la même chose que nous faisions dans la version XHR — définir le contenu texte de l'élément {{htmlelement("pre")}} au texte récupéré.

- -

À propos des promesses

- -

Les promesses peuvent être un peu déroutantes au premier abord, ne vous en souciez pas trop pour l'instant. Vous vous y ferez après un certain temps, d'autant plus après en avoir appris davantage sur les APIs JavaScript modernes — la plupart des APIs récentes utilisent beaucoup les promesses.

- -

Regardons à nouveau la structure d'une promesse pour voir si nous pouvons en donner plus de sens.

- -

Promesse 1

- -
fetch(url).then(function(response) {
-  //...
-});
- -

Si l'on traduit en bon français les instructions JavaScript, on pourrait dire

- - - -

On dit qu'une promesse est "résolue" (resolved) lorsque l'opération spécifiée à un moment donné est terminée. En l'occurence, l'opération spécifiée est de récupérer une ressource à une URL donnée (en utilisant une requête HTTP) et de retourner la réponse reçue du serveur.

- -

La fonction passée à then() n'est pas exécutée immédiatement — elle est exécutée à un moment dans le futur, dès que la promesse est résolue (c'est à dire qu'elle a retourné la réponse du serveur).

- -

Notez que vous pouvez également choisir de stocker votre promesse dans une variable, et de chaîner le {{jsxref("Promise.then",".then()")}} sur cette variable. L'exemple suivant fait la même chose que le précédent :

- -
var myFetch = fetch(url);
-
-myFetch.then(function(response) {
-  //...
-});
- -

Parce que la méthode fetch() retourne une promesse qui résout une réponse HTTP, la fonction définie à l'intérieur du .then() reçoit la réponse en tant que paramètre. Vous pouvez appeler le paramètre comme vous souhaitez — l'exemple ci-dessous fait toujours la même chose :

- -
fetch(url).then(function(dogBiscuits) {
-  //...
-});
- -

Mais il est plus logique de donner un nom de paramètre qui décrit son contenu !

- -

Promesse 2

- -

Voyons maintenant la fonction appelé dans .then():

- -
function(response) {
-  response.text().then(function(text) {
-    poemDisplay.textContent = text;
-  });
-}
- -

L'objet response a une méthode {{domxref("Body.text","text()")}}, qui convertit les données brutes contenues dans la réponse en texte brut — c'est le format que nous voulons. Cette méthode retourne également une promesse, qui se résout lorsque la réponse est convertie en texte, nous utilisons donc un deuxième {{jsxref("Promise.then",".then()")}} pour cette deuxième promesse.

- -

À l'intérieur de ce dernier .then(), nous définissons une nouvelle fonction, qui décide de ce que nous faisons avec le texte récupéré. Nous nous contentons de définir la propriété textContent de l'élément {{htmlelement("pre")}} à la valeur du texte.

- -

Chaîner les then()

- -

Notez que le résultat de la fonction appelée par le .then() est également retourné par ce dernier, nous pouvons donc mettre les .then() bout à bout, en passant le résultat du bloc précédent au prochain.

- -

Ainsi, le bloc de code suivant fait la même chose que notre exemple original, mais écrit dans un style différent :

- -
fetch(url).then(function(response) {
-  return response.text()
-}).then(function(text) {
-  poemDisplay.textContent = text;
-});
- -

Beaucoup de développeurs préfèrent ce style, plus "plat" : il évite de définir des fonctions à l'intérieur de fonctions et est plus facile à lire lorsqu'il y a beaucoup de promesses qui s'enchaînent. La seule différence ici est que nous avons une instruction return pour retourner response.text(), et ce résultat est passé au prochain .then().

- -

Quel mécanisme devriez-vous utiliser?

- -

Cela dépend du projet sur lequel vous travaillez. XHR existe depuis longtemps maintenant et bénéficie d'un très bon support sur les différents navigateurs. Fetch et les promesses, en revanche, sont un ajout plus récent à la plateforme web, bien qu'ils soient pris en charge par la plupart des navigateurs, Internet Explorer et Safari font exception.

- -

Si vous voulez un support des anciens navigateurs, alors XHR est probablement la solution préférable. En revanche, si vous travaillez sur un projet plus progressif, et que vous n'êtes pas tant préoccupé par les anciens navigateurs, alors Fetch peut être un bon choix.

- -

Vous devriez apprendre les deux alternatives — Fetch deviendra plus populaire au fur et à mesure que l'utilisation d'Internet Explorer diminue (IE n'est plus développé, en faveur du nouveau navigateur de Microsoft, Edge), mais vous allez avoir besoin de XHR pendant un moment encore.

- -

Un exemple plus complexe

- -

Pour clore l'article, nous allons regarder un exemple un peu plus complexe, qui montre des utilisations plus intéressantes de Fetch. Nous avons créé un site d'exemple appelé The Can Store (le magasin de conserves) — c'est un supermarché fictif qui ne vend que des boites de conserves. Vous pouvez trouver cet exemple en direct sur GitHub, et voir le code source.

- -

A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column.

- -

Par défaut, le site affiche tous les produits ; mais vous pouvez utiliser le formulaire dans la colonne de gauche pour les filtrer par catégorie, ou chercher un terme, ou les deux.

- -

Il y a du code plutôt complexe pour traiter le filtrage des produits par catégorie et par terme de recherche, manipulant les chaînes de caractères pour afficher les données correctement dans l'interface utilisateur, etc. Nous n'allons pas en discuter dans cet article, mais vous pouvez trouver des commentaires très complets dans le code (voir can-script.js). Nous allons expliquer le code Fetch.

- -

Premier Fetch

- -

Le premier bloc qui utilise Fetch se trouve au début du JavaScript :

- -
fetch('products.json').then(function(response) {
-  if(response.ok) {
-    response.json().then(function(json) {
-      products = json;
-      initialize();
-    });
-  } else {
-    console.log('Network request for products.json failed with response ' + response.status + ': ' + response.statusText);
-  }
-});
- -

Cela ressemble à ce que vous avons vu précédemment, sauf que la deuxième promesse est à l'intérieur d'une condition. Cette condition vérifie si la réponse retournée est un succès ou non — la propriété {{domxref("response.ok")}} contient un booléen qui vaut true si le statut de la réponse était OK (statut HTTP 200, "OK"), ou false sinon.

- -

Si la réponse était un succès, nous déclenchons la deuxième promesse — cette fois-ci en utilisant {{domxref("Body.json","json()")}} et non {{domxref("Body.text","text()")}}, puisque nous voulons récupérer la réponse sous forme de données structurées JSON et non de texte brut.

- -

Si la réponse n'est pas un succès, nous affichons une erreur dans la console indiquant que la requête réseau a échoué, avec le statut et le message obtenus (contenus dans les propriétés {{domxref("response.status")}} et {{domxref("response.statusText")}} respectivement). Bien sûr, un véritable site web traiterait cette erreur plus gracieusement, en affichant un message à l'écran en offrant peut-être des options pour remédier à la situation.

- -

Vous pouvez tester le cas d'échec vous-même :

- -
    -
  1. Faites une copie locale des fichiers d'exemple (téléchargez et dézippez le fichier ZIP can-store)
  2. -
  3. Éxecutez le code via un serveur web (comme vu précédemment dans {{anch("Serving your example from a server", "Servir votre exemple depuis un serveur")}})
  4. -
  5. Modifiez le chemin du fichier à récupérer, mettez un nom de fichier qui n'existe pas, comme 'produc.json'.
  6. -
  7. Maintenant, chargez le fichier index dans votre navigateur (via localhost:8000) et regardez dans la console de développement. Vous verrez un message parmi les lignes "Network request for products.json failed with response 404: File not found" (la requête réseau pour products.json a échoué avec la réponse 404: Fichier non trouvé).
  8. -
- -

Deuxième Fetch

- -

Le deuxième bloc Fetch se trouve dans la fonction fetchBlob():

- -
fetch(url).then(function(response) {
-  if(response.ok) {
-    response.blob().then(function(blob) {
-      objectURL = URL.createObjectURL(blob);
-      showProduct(objectURL, product);
-    });
-  } else {
-    console.log('Network request for "' + product.name + '" image failed with response ' + response.status + ': ' + response.statusText);
-  }
-});
- -

Cela fonctionne à peu près de la même manière que le précédent, sauf qu'au lieu d'utiliser {{domxref("Body.json","json()")}}, on utilise {{domxref("Body.blob","blob()")}} — en l'occurence, nous voulons récupérer la réponse sous la forme d'un fichier image, et le format de données que nous utilisons est Blob — ce terme est une abbréviation de "Binary Large Object" (large objet binaire) et peut être utilisé pour représenter de gros objets fichier — tels que des fichiers images ou vidéo.

- -

Une fois que nous avons reçu notre blob avec succès, nous créons un objet URL, en utilisant {{domxref("URL.createObjectURL()", "createObjectURL()")}}. Cela renvoie une URL interne temporaire qui pointe vers un blob en mémoire dans le navigateur. Cet objet n'est pas très lisible, mais vous pouvez voir à quoi il ressemble en ouvrant l'application Can Store, Ctrl + Clic droit sur l'image, et sélectionner l'option "Afficher l'image" (peut légèrement varier selon le navigateur que vous utilisez). L'URL créée sera visible à l'intérieur de la barre d'adresse et devrait ressembler à quelque chose comme ça :

- -
blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4
- -

Challenge : une version XHR de Can Store

- -

Comme exercice pratique, nous aimerions que vous essayiez de convertir la version Fetch de l'application en une version XHR. Faites une copie du fichier ZIP et essayiez de modifier le JavaScript en conséquence.

- -

Quelques conseils qui pourraient s'avérer utiles :

- - - -
-

Note : Si vous avez des difficultés à le faire, vous pouvez comparer votre code à la version finale sur GitHub (voir le code source, ou voir en direct).

-
- -

Sommaire

- -

Voilà qui clôt notre article sur la récupération de données à partir du serveur. À ce stade, vous devriez savoir comment travailler avec XHR et Fetch.

- -

Voir aussi

- -

Il y a beaucoup de sujets abordés dans cet article, dont nous n'avons qu'égratigné la surface. Pour plus de détails sur ces sujets, essayez les articles suivants:

- - - -
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
- -
-

Dans ce module

- - -
diff --git a/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.md b/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.md new file mode 100644 index 0000000000..9fe8ea07d5 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/fetching_data/index.md @@ -0,0 +1,398 @@ +--- +title: Récupérer des données du serveur +slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data +tags: + - API + - Apprendre + - Article + - Codage + - Débutant + - Fetch + - JavaScript + - XHR + - data +translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Fetching_data +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

Une autre tâche courante dans les sites et applications web modernes est de récupérer des données à partir du serveur pour mettre à jour des sections de la page web sans la recharger entièrement. Ce qui pourrait paraître comme un petit détail a, en vérité, eu un impact énorme sur les performances et le comportement des sites. Dans cet article, nous allons expliquer le concept et les technologies qui rendent cela possible, tels que XMLHttpRequest et l'API Fetch.

+ + + + + + + + + + + + +
Prérequis :Notions de base de JavaScript (voir premiers pas, les briques JavaScript, les objets JavaScript), les notions de bases des APIs côté client
Objectif :Apprendre à récupérer des données du serveur web et les utiliser pour mettre à jour le contenu de la page.
+ +

Quel est le problème?

+ +

À la base, le chargement d'une page web est simple — vous envoyez une requête à un serveur et, tant qu'il n'y a pas eu de problème, les ressources de la page web sont téléchargées et affichées sur votre ordinateur.

+ +

A basic representation of a web site architecture

+ +

Le problème avec ce modèle c'est qu'à chaque fois que vous voulez mettre à jour une partie de la page, par exemple pour afficher la page suivante d'une liste de produits, vous devez recharger toute la page. Ce surcoût est tout à fait inutile et résulte en une mauvaise expérience utilisateur, particulièrement pour les pages qui sont lourdes, complexes et du coup longues à charger.

+ +

L'arrivée d'Ajax

+ +

Pour traiter ce problème, des technologies ont été élaborées qui permettent de récupérer à la demande de petites portions de données (comme du HTML, {{glossary("XML")}}, JSON, ou texte brut) et de les afficher dans la page web.

+ +

Nous avons pour cela l'API {{domxref("XMLHttpRequest")}} à notre disposition ou — plus récemment — l'API Fetch. Elles permettent de réaliser des requêtes HTTP pour récupérer des ressources spécifiques disponibles sur un serveur et de formater les données retournées selon les besoins avant l'affichage dans la page.

+ +
+

Note : Dans les premiers temps, cette technique était appelée "Asynchronous JavaScript and XML" (JavaScript asychrone et XML), dit AJAX, parce qu'elle utilisait {{domxref("XMLHttpRequest")}} pour requêter des données XML. De nos jours, c'est rarement le cas; la plupart du temps, on utilise XMLHttpRequest ou Fetch pour requêter des données JSON. Quoi qu'il en soit, le procédé reste le même et le terme "Ajax" est resté pour décrire cette technique.

+
+ +

A simple modern architecture for web sites

+ +

Le modèle Ajax implique une API web comme proxy pour requêter les données plus intelligemment que simplement rafraîchir la page à chaque fois. Voyons plutôt l'impact que cela a :

+ +
    +
  1. Allez sur un site riche en information de votre choix, comme Amazon, YouTube, CNN...
  2. +
  3. Cherchez quelque chose dans la barre de recherche, comme un nouveau produit. Le contenu principal va changer, mais la plupart de ce qui l'entoure reste statique, comme l'entête, le pied de page, le menu de navigation, etc.
  4. +
+ +

C'est une bonne chose puisque :

+ + + +

Notez que pour accélerer les choses encore davantage, certains sites stockent les ressources et données chez le client lors de sa première visite, si bien que les visites suivantes, les fichiers locaux sont utilisés et non re-téléchargés du serveur. Le contenu n'est rechargé que lorsqu'il a été mis à jour sur le serveur.

+ +

A basic web app data flow architecture

+ +

Une requête Ajax basique

+ +

Voyons maintenant comment ces requêtes sont gérées, en utilisant soit {{domxref("XMLHttpRequest")}} soit Fetch. Pour ces exemples, nous allons requêter les données de différents fichiers texte et les utiliserons pour remplir une zone de contenu.

+ +

Ces fichiers agiront comme une fausse base de données ; dans une vraie application, il est plus probable que vous utiliseriez un langage côté serveur comme PHP, Python, ou Node pour récupérer les données à partir d'une véritable base de données. En revanche, nous voulons ici garder les choses simples ; nous allons donc nous concentrer sur le côté client.

+ +

XMLHttpRequest

+ +

XMLHttpRequest (qui est fréquemment abrégé XHR) est une technologie assez vieille maintenant — elle a été inventée par Microsoft dans les années 90 et a été standardisée dans les autres navigateurs il y a longtemps.

+ +
    +
  1. +

    Pour commencer cet exemple, faites une copie locale de ajax-start.html et des quatre fichiers texte — verse1.txt, verse2.txt, verse3.txt, et verse4.txt — dans un nouveau répertoire sur votre ordinateur. Dans cet exemple, nous allons charger le verset d'un poème (que vous pourriez bien reconnaître), quand il est sélectionné dans le menu déroulant, en utilisant XHR.

    +
  2. +
  3. +

    À l'intérieur de l'élément {{htmlelement("script")}}, ajoutez le code qui suit. Il stocke une référence aux éléments {{htmlelement("select")}} et {{htmlelement("pre")}} dans des variables et définit un gestionnaire d'événement {{domxref("GlobalEventHandlers.onchange","onchange")}}, pour que, quand la valeur du menu déroulant est changée, la valeur sélectionnée soit passée comme paramètre à la fonction  updateDisplay().

    + +
    var verseChoose = document.querySelector('select');
    +var poemDisplay = document.querySelector('pre');
    +
    +verseChoose.onchange = function() {
    +  var verse = verseChoose.value;
    +  updateDisplay(verse);
    +};
    +
  4. +
  5. +

    Définissons maintenant la fonction updateDisplay(). Tout d'abord, mettez ce qui suit au bas de votre JavaScript — c'est la structure vide de la fonction :

    + +
    function updateDisplay(verse) {
    +
    +};
    +
  6. +
  7. +

    Nous allons commencer notre fonction en construisant une URL relative qui pointe vers le fichier texte que nous voulons charger, nous en aurons besoin plus tard. La valeur de l'élément {{htmlelement("select")}} à tout instant est la même que l'élément {{htmlelement("option")}} sélectionné (c'est à dire le texte de l'élément sélectionné, ou son attribut value s'il est spécifié) — par exemple "Verse 1". Le fichier correspondant est "verse1.txt" et il est situé dans le même répertoire que le fichier HTML, le nom du fichier seul suffira donc.

    + +

    Les serveurs web sont généralement sensibles à la casse, le nom de fichier n'a pas d'espace et a une extension de fichier. Pour convertir "Verse 1" en "verse1.txt" nous allons convertir le "V" en minuscles avec {{jsxref("String.toLowerCase", "toLowerCase()")}}, supprimer l'espace avec {{jsxref("String.replace", "replace()")}} et ajouter ".txt" à la fin avec une simple concaténation de chaînes. Ajoutez les lignes suivantes à l'intérieur de la fonction updateDisplay() :

    + +
    verse = verse.replace(" ", "");
    +verse = verse.toLowerCase();
    +var url = verse + '.txt';
    +
  8. +
  9. +

    Pour commencer à créer une requête XHR, vous allez devoir créer un nouvel objet avec le constructeur {{domxref("XMLHttpRequest.XMLHttpRequest", "XMLHttpRequest()")}}. Vous pouvez appeler cet objet comme vous le voulez, mais nous l'appellerons request pour plus de clarté. Ajoutez ce qui suit à vos lignes précédentes :

    + +
    var request = new XMLHttpRequest();
    +
  10. +
  11. +

    Ensuite, vous allez devoir utiliser la méthode {{domxref("XMLHttpRequest.open","open()")}} pour spécifier la méthode HTTP et l'URL à utiliser pour récupérer la ressource. Nous allons ici utiliser la méthode GET et passer notre variable url pour URL. Ajoutez ceci à la suite de la ligne précédente :

    + +
    request.open('GET', url);
    +
  12. +
  13. +

    Nous allons définir le type de réponse que nous attendons — définit par la propriété {{domxref("XMLHttpRequest.responseType", "responseType")}} de la requête — comme text. Ce n'est pas strictement nécessaire ici — XHR retourne du texte par défaut — mais c'est une bonne idée d'en prendre l'habitude pour les cas où vous aurez besoin de définir un type différent. Ajoutez ceci à la suite :

    + +
    request.responseType = 'text';
    +
  14. +
  15. +

    Récupérer une ressource sur le réseau est une opération {{glossary("asynchronous","asynchrone")}}, ce qui signifie que vous devez attendre que cette opération se termine (par exemple, que la ressource soit renvoyée) avant de pouvoir récupérer la réponse — sans quoi une erreur est levée. XHR permet d'exécuter du code lorsque la réponse est reçue grâce au gestionnaire d'événement {{domxref("XMLHttpRequest.onload", "onload")}} — quand l'événement {{event("load")}} est déclenché. Une fois que la réponse a été reçue, alors la réponse est accessible via la propriété response de l'objet XHR utilisé.

    + +

    Ajoutez le bloc de code qui suit toujours au bas de la fonction updateDisplay(). Vous verrez qu'à l'intérieur du gestionnaire d'événément onload, nous assignons la propriété textContent de poemDisplay (l'élément {{htmlelement("pre")}}) à la valeur de la propriété {{domxref("XMLHttpRequest.response", "request.response")}}.

    + +
    request.onload = function() {
    +  poemDisplay.textContent = request.response;
    +};
    +
  16. +
  17. +

    Les étapes précédentes nous ont permis de configurer la requête XHR, mais celle-ci n'est exécutée que lorsqu'on le demande explicitement. Pour ce faire, il faut appeler la méthode {{domxref("XMLHttpRequest.send","send()")}}. Ajoutez la ligne suivante à la suite du code déjà écrit :

    + +
    request.send();
    + +

    Voyez la section {{anch("Serving your example from a server", "Servir votre exemple depuis un serveur")}} pour pouvoir tester.

    +
  18. +
  19. +

    Un dernier problème avec cet exemple est qu'il ne montre rien au chargement de la page (mais uniquement à la sélection d'un verset). Pour corriger cela, ajoutez ce qui suit au bas de votre code (juste au-dessus de la balise fermante </script>), pour charger le verset 1 par défaut, et s'assurer que l'élément {{htmlelement("select")}} montre toujours la bonne valeur :

    + +
    updateDisplay('Verse 1');
    +verseChoose.value = 'Verse 1';
    +
  20. +
+ +

Servir votre exemple depuis un serveur

+ +

Certains navigateurs (dont Chrome) n'exécuteront pas de requêtes XHR si vous exécutez votre script simplement à partir d'un fichier local. Cela est dû à des restrictions de sécurité (pour plus d'infos sur la sécurité web, consultez l'article La sécurité d'un site web).

+ +

Pour règler ce problème, vous devez tester l'exemple à travers un serveur web local. Pour savoir comment procéder, lisez Comment configurer un serveur de test local?

+ +

Fetch

+ +

L'API Fetch est une solution moderne qui vient remplacer XHR — elle a été introduite récemment dans les navigateurs pour rendre les requêtes HTTP asynchrones plus simples en JavaScript, à la fois pour les développeurs et pour les autres APIs qui utilisent cette technologie.

+ +

Voyons comment convertir le dernier exemple, en remplaçant XHR par Fetch.

+ +
    +
  1. +

    Faites une copie du répertoire de votre dernier exemple. (Ou si vous ne l'avez pas fait, créez un nouveau répertoire et copiez le fichier xhr-basic.html et les quatre fichiers texte — verse1.txt, verse2.txt, verse3.txt, and verse4.txt — à l'intérieur).

    +
  2. +
  3. +

    À l'intérieur de la fonction updateDisplay(), vous avez le code XHR suivant :

    + +
    var request = new XMLHttpRequest();
    +request.open('GET', url);
    +request.responseType = 'text';
    +
    +request.onload = function() {
    +  poemDisplay.textContent = request.response;
    +};
    +
    +request.send();
    +
  4. +
  5. +

    Remplacez-le avec ce qui suit :

    + +
    fetch(url).then(function(response) {
    +  response.text().then(function(text) {
    +    poemDisplay.textContent = text;
    +  });
    +});
    +
  6. +
  7. +

    Chargez l'exemple dans votre navigateur (en l'exécutant à travers un serveur web) et il devrait produire le même résultat que la version XHR  — pourvu que vous utilisiez un navigateur moderne.

    +
  8. +
+ +

Que se passe-t-il dans le code Fetch?

+ +

Tout d'abord, nous invoquons la méthode {{domxref("WindowOrWorkerGlobalScope.fetch()","fetch()")}}, en lui passant l'URL de la ressource que nous voulons récupérer. C'est la version moderne équivalente à {{domxref("XMLHttpRequest.open","request.open()")}} de XHR, et n'avez pas à appeler .send() — la requête est exécutée directemment.

+ +

Ensuite, la méthode {{jsxref("Promise.then",".then()")}} est chaînée à la suite de fetch() — cette méthode fait partie des {{jsxref("Promise","Promesses")}}, une fonctionnalité JavaScript moderne qui permet d'effectuer des opérations asynchrones. fetch() retourne une promesse, qui est résolue lorsque la réponse est reçue du serveur — et nous utilisons .then() pour exécuter du code à ce moment là. C'est l'équivalent du gestionnaire d'événément onload dans la version XHR.

+ +

La fonction définie dans le .then() reçoit la réponse du serveur comme paramètre, une fois que la promesse retournée par fetch() est résolue. À l'intérieur de cette fonction, nous utilisons la méthode {{domxref("Body.text","text()")}} pour récupérer le contenu de la réponse en texte brut. C'est l'équivalent de request.responseType = 'text' dans la version XHR.

+ +

Vous verrez que text() retourne également une promesse, nous y chaînons donc un nouveau .then(), à l'intérieur de quoi nous définissons une fonction. Cette dernière récupère quant à elle le texte brut que la promesse précédente résout.

+ +

Enfin, dans le corps de la fonction, nous faisons la même chose que nous faisions dans la version XHR — définir le contenu texte de l'élément {{htmlelement("pre")}} au texte récupéré.

+ +

À propos des promesses

+ +

Les promesses peuvent être un peu déroutantes au premier abord, ne vous en souciez pas trop pour l'instant. Vous vous y ferez après un certain temps, d'autant plus après en avoir appris davantage sur les APIs JavaScript modernes — la plupart des APIs récentes utilisent beaucoup les promesses.

+ +

Regardons à nouveau la structure d'une promesse pour voir si nous pouvons en donner plus de sens.

+ +

Promesse 1

+ +
fetch(url).then(function(response) {
+  //...
+});
+ +

Si l'on traduit en bon français les instructions JavaScript, on pourrait dire

+ + + +

On dit qu'une promesse est "résolue" (resolved) lorsque l'opération spécifiée à un moment donné est terminée. En l'occurence, l'opération spécifiée est de récupérer une ressource à une URL donnée (en utilisant une requête HTTP) et de retourner la réponse reçue du serveur.

+ +

La fonction passée à then() n'est pas exécutée immédiatement — elle est exécutée à un moment dans le futur, dès que la promesse est résolue (c'est à dire qu'elle a retourné la réponse du serveur).

+ +

Notez que vous pouvez également choisir de stocker votre promesse dans une variable, et de chaîner le {{jsxref("Promise.then",".then()")}} sur cette variable. L'exemple suivant fait la même chose que le précédent :

+ +
var myFetch = fetch(url);
+
+myFetch.then(function(response) {
+  //...
+});
+ +

Parce que la méthode fetch() retourne une promesse qui résout une réponse HTTP, la fonction définie à l'intérieur du .then() reçoit la réponse en tant que paramètre. Vous pouvez appeler le paramètre comme vous souhaitez — l'exemple ci-dessous fait toujours la même chose :

+ +
fetch(url).then(function(dogBiscuits) {
+  //...
+});
+ +

Mais il est plus logique de donner un nom de paramètre qui décrit son contenu !

+ +

Promesse 2

+ +

Voyons maintenant la fonction appelé dans .then():

+ +
function(response) {
+  response.text().then(function(text) {
+    poemDisplay.textContent = text;
+  });
+}
+ +

L'objet response a une méthode {{domxref("Body.text","text()")}}, qui convertit les données brutes contenues dans la réponse en texte brut — c'est le format que nous voulons. Cette méthode retourne également une promesse, qui se résout lorsque la réponse est convertie en texte, nous utilisons donc un deuxième {{jsxref("Promise.then",".then()")}} pour cette deuxième promesse.

+ +

À l'intérieur de ce dernier .then(), nous définissons une nouvelle fonction, qui décide de ce que nous faisons avec le texte récupéré. Nous nous contentons de définir la propriété textContent de l'élément {{htmlelement("pre")}} à la valeur du texte.

+ +

Chaîner les then()

+ +

Notez que le résultat de la fonction appelée par le .then() est également retourné par ce dernier, nous pouvons donc mettre les .then() bout à bout, en passant le résultat du bloc précédent au prochain.

+ +

Ainsi, le bloc de code suivant fait la même chose que notre exemple original, mais écrit dans un style différent :

+ +
fetch(url).then(function(response) {
+  return response.text()
+}).then(function(text) {
+  poemDisplay.textContent = text;
+});
+ +

Beaucoup de développeurs préfèrent ce style, plus "plat" : il évite de définir des fonctions à l'intérieur de fonctions et est plus facile à lire lorsqu'il y a beaucoup de promesses qui s'enchaînent. La seule différence ici est que nous avons une instruction return pour retourner response.text(), et ce résultat est passé au prochain .then().

+ +

Quel mécanisme devriez-vous utiliser?

+ +

Cela dépend du projet sur lequel vous travaillez. XHR existe depuis longtemps maintenant et bénéficie d'un très bon support sur les différents navigateurs. Fetch et les promesses, en revanche, sont un ajout plus récent à la plateforme web, bien qu'ils soient pris en charge par la plupart des navigateurs, Internet Explorer et Safari font exception.

+ +

Si vous voulez un support des anciens navigateurs, alors XHR est probablement la solution préférable. En revanche, si vous travaillez sur un projet plus progressif, et que vous n'êtes pas tant préoccupé par les anciens navigateurs, alors Fetch peut être un bon choix.

+ +

Vous devriez apprendre les deux alternatives — Fetch deviendra plus populaire au fur et à mesure que l'utilisation d'Internet Explorer diminue (IE n'est plus développé, en faveur du nouveau navigateur de Microsoft, Edge), mais vous allez avoir besoin de XHR pendant un moment encore.

+ +

Un exemple plus complexe

+ +

Pour clore l'article, nous allons regarder un exemple un peu plus complexe, qui montre des utilisations plus intéressantes de Fetch. Nous avons créé un site d'exemple appelé The Can Store (le magasin de conserves) — c'est un supermarché fictif qui ne vend que des boites de conserves. Vous pouvez trouver cet exemple en direct sur GitHub, et voir le code source.

+ +

A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column.

+ +

Par défaut, le site affiche tous les produits ; mais vous pouvez utiliser le formulaire dans la colonne de gauche pour les filtrer par catégorie, ou chercher un terme, ou les deux.

+ +

Il y a du code plutôt complexe pour traiter le filtrage des produits par catégorie et par terme de recherche, manipulant les chaînes de caractères pour afficher les données correctement dans l'interface utilisateur, etc. Nous n'allons pas en discuter dans cet article, mais vous pouvez trouver des commentaires très complets dans le code (voir can-script.js). Nous allons expliquer le code Fetch.

+ +

Premier Fetch

+ +

Le premier bloc qui utilise Fetch se trouve au début du JavaScript :

+ +
fetch('products.json').then(function(response) {
+  if(response.ok) {
+    response.json().then(function(json) {
+      products = json;
+      initialize();
+    });
+  } else {
+    console.log('Network request for products.json failed with response ' + response.status + ': ' + response.statusText);
+  }
+});
+ +

Cela ressemble à ce que vous avons vu précédemment, sauf que la deuxième promesse est à l'intérieur d'une condition. Cette condition vérifie si la réponse retournée est un succès ou non — la propriété {{domxref("response.ok")}} contient un booléen qui vaut true si le statut de la réponse était OK (statut HTTP 200, "OK"), ou false sinon.

+ +

Si la réponse était un succès, nous déclenchons la deuxième promesse — cette fois-ci en utilisant {{domxref("Body.json","json()")}} et non {{domxref("Body.text","text()")}}, puisque nous voulons récupérer la réponse sous forme de données structurées JSON et non de texte brut.

+ +

Si la réponse n'est pas un succès, nous affichons une erreur dans la console indiquant que la requête réseau a échoué, avec le statut et le message obtenus (contenus dans les propriétés {{domxref("response.status")}} et {{domxref("response.statusText")}} respectivement). Bien sûr, un véritable site web traiterait cette erreur plus gracieusement, en affichant un message à l'écran en offrant peut-être des options pour remédier à la situation.

+ +

Vous pouvez tester le cas d'échec vous-même :

+ +
    +
  1. Faites une copie locale des fichiers d'exemple (téléchargez et dézippez le fichier ZIP can-store)
  2. +
  3. Éxecutez le code via un serveur web (comme vu précédemment dans {{anch("Serving your example from a server", "Servir votre exemple depuis un serveur")}})
  4. +
  5. Modifiez le chemin du fichier à récupérer, mettez un nom de fichier qui n'existe pas, comme 'produc.json'.
  6. +
  7. Maintenant, chargez le fichier index dans votre navigateur (via localhost:8000) et regardez dans la console de développement. Vous verrez un message parmi les lignes "Network request for products.json failed with response 404: File not found" (la requête réseau pour products.json a échoué avec la réponse 404: Fichier non trouvé).
  8. +
+ +

Deuxième Fetch

+ +

Le deuxième bloc Fetch se trouve dans la fonction fetchBlob():

+ +
fetch(url).then(function(response) {
+  if(response.ok) {
+    response.blob().then(function(blob) {
+      objectURL = URL.createObjectURL(blob);
+      showProduct(objectURL, product);
+    });
+  } else {
+    console.log('Network request for "' + product.name + '" image failed with response ' + response.status + ': ' + response.statusText);
+  }
+});
+ +

Cela fonctionne à peu près de la même manière que le précédent, sauf qu'au lieu d'utiliser {{domxref("Body.json","json()")}}, on utilise {{domxref("Body.blob","blob()")}} — en l'occurence, nous voulons récupérer la réponse sous la forme d'un fichier image, et le format de données que nous utilisons est Blob — ce terme est une abbréviation de "Binary Large Object" (large objet binaire) et peut être utilisé pour représenter de gros objets fichier — tels que des fichiers images ou vidéo.

+ +

Une fois que nous avons reçu notre blob avec succès, nous créons un objet URL, en utilisant {{domxref("URL.createObjectURL()", "createObjectURL()")}}. Cela renvoie une URL interne temporaire qui pointe vers un blob en mémoire dans le navigateur. Cet objet n'est pas très lisible, mais vous pouvez voir à quoi il ressemble en ouvrant l'application Can Store, Ctrl + Clic droit sur l'image, et sélectionner l'option "Afficher l'image" (peut légèrement varier selon le navigateur que vous utilisez). L'URL créée sera visible à l'intérieur de la barre d'adresse et devrait ressembler à quelque chose comme ça :

+ +
blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4
+ +

Challenge : une version XHR de Can Store

+ +

Comme exercice pratique, nous aimerions que vous essayiez de convertir la version Fetch de l'application en une version XHR. Faites une copie du fichier ZIP et essayiez de modifier le JavaScript en conséquence.

+ +

Quelques conseils qui pourraient s'avérer utiles :

+ + + +
+

Note : Si vous avez des difficultés à le faire, vous pouvez comparer votre code à la version finale sur GitHub (voir le code source, ou voir en direct).

+
+ +

Sommaire

+ +

Voilà qui clôt notre article sur la récupération de données à partir du serveur. À ce stade, vous devriez savoir comment travailler avec XHR et Fetch.

+ +

Voir aussi

+ +

Il y a beaucoup de sujets abordés dans cet article, dont nous n'avons qu'égratigné la surface. Pour plus de détails sur ces sujets, essayez les articles suivants:

+ + + +
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}
+ +
+

Dans ce module

+ + +
diff --git a/files/fr/learn/javascript/client-side_web_apis/index.html b/files/fr/learn/javascript/client-side_web_apis/index.html deleted file mode 100644 index b7ce9e7171..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/index.html +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: API web utilisées côté client -slug: Learn/JavaScript/Client-side_web_APIs -tags: - - API - - API Web - - Apprendre - - Articles - - Codage - - DOM - - Débutant - - Graphismes - - JavaScript - - Localisation - - Media - - Module - - données -translation_of: Learn/JavaScript/Client-side_web_APIs -original_slug: Apprendre/JavaScript/Client-side_web_APIs ---- -
{{LearnSidebar}}
- -

Lorsque vous écrivez du JavaScript côté client pour des sites Web ou des applications, vous n'irez pas très loin avant d'utiliser des API - des interfaces pour manipuler différents aspects du navigateur et du système d'exploitation sur lesquels le site opère, ou même des données provenant d'autres sites web ou services. Dans ce module, nous allons explorer ce que sont les API, et comment utiliser certaines API les plus courantes que vous rencontrerez souvent dans votre travail de développement.

- -

Prérequis

- -

Pour tirer le meilleur parti de ce module, vous devriez avoir parcouru les précédents modules JavaScript de la série (Premiers pas, Building blocks et objets JavaScript). Ces modules impliquent tout de même un bon nombre d'utilisations simples de l'API, car il est difficile d'écrire des exemples JavaScript côté client faisant quelque chose d'utile sans eux! Ici, nous passons à un niveau supérieur, en supposant que vous connaissiez le langage JavaScript de base et explorant les APIs Web courantes de manière un peu plus détaillée.

- -

Une connaissance basique de HTML et CSS serait aussi utile.

- -
-

Note : Si vous travaillez sur un ordinateur/tablette/autre périphérique où vous n'avez pas la possibilité de créer vos propres fichiers, vous pouvez essayer (la plupart) des exemples de code dans un programme de code en ligne tel que JSBin ou Thimble.

-
- -

Guides

- -
-
Introduction aux API du Web
-
Tout d'abord, nous survolerons du concept d'API — qu'est-ce que c'est, comment ça fonctionne, comment les utiliser dans votre code, et comment sont-elles structurées. Nous verrons également quelles sont les principales API et leur utilisation.
-
Manipuler des documents
-
Quand on écrit des pages web et des applications, une des choses les plus courantes que l'on veut faire est de manipuler la structure du document d'une manière ou d'une autre. On le fait généralement en utilisant le Document Object Model (DOM), un ensemble d'APIs qui permettent de contrôler le HTML et le style — et qui utilisent massivement l'objet {{domxref("Document")}}. Dans cet article, nous allons voir comment utiliser le DOM en détail, ainsi que quelques APIs intéressantes qui peuvent modifier votre environnement.
-
Récupérer des données du serveur
-
Une autre tâche courante dans les sites et applications web modernes est de récupérer des données à partir du serveur pour mettre à jour des sections de la page web sans la recharger entièrement. Ce qui pourrait paraître comme un petit détail, a en vérité eu un impact énorme sur les performances et le comportement des sites. Dans cet article, nous allons expliquer le concept et les technologies qui rendent cela possible, tels que {{domxref("XMLHttpRequest")}} et l'API Fetch.
-
APIs tierces
-
Les APIs que nous avons vu jusqu'à présent sont intégrées dans le navigateur, mais ce n'est pas le cas de toutes. De nombreux gros sites web tels que Google Maps, Twitter, Facebook, PayPal, etc, fournissent des APIs permettant aux développeurs d'utiliser leurs données (par exemple pour afficher un flux twitter sur un blog) ou service (par exemple afficher une carte Google Maps sur un site, ou utiliser Facebook pour permettre aux utilisateurs de se connecter). Cet article compare les APIs du navigateur aux APIs tierces et montre quelques utilisations typiques de ces dernières.
-
Dessiner des éléments graphiques
-
Le navigateur contient des outils de programmation graphique très puissants, du langage SVG (Scalable Vector Graphics), aux APIs pour dessiner sur les éléments HTML {{htmlelement("canvas")}}, (voir API Canvas et WebGL). Cet article fournit une introduction à canvas et introduit d'autres ressources pour vous permettre d'en apprendre plus.
-
APIs vidéo et audio
-
HTML5 fournit des éléments pour intégrer du multimédia dans les documents — {{htmlelement("video")}} et {{htmlelement("audio")}} — et qui viennent avec leurs propres APIs pour contrôler la lecture, se déplacer dans le flux, etcCet article montre comment réaliser les tâches les plus communes, comme créer des contrôles de lectures personnalisés.
-
Stockage côté client
-
Les navigateurs web modernes permettent aux sites web de stocker des données sur l'ordinateur de l'utilisateur — avec sa permission — puis de les récupérer au besoin. Cela permet d'enregistrer des données pour du stockage long-terme, de sauvegarder des documents ou sites hors-ligne, de conserver des préférences spécifiques à l'utilisateur, et plus encore. Cet article explique les fondamentaux pour y parvenir.
-
diff --git a/files/fr/learn/javascript/client-side_web_apis/index.md b/files/fr/learn/javascript/client-side_web_apis/index.md new file mode 100644 index 0000000000..b7ce9e7171 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/index.md @@ -0,0 +1,52 @@ +--- +title: API web utilisées côté client +slug: Learn/JavaScript/Client-side_web_APIs +tags: + - API + - API Web + - Apprendre + - Articles + - Codage + - DOM + - Débutant + - Graphismes + - JavaScript + - Localisation + - Media + - Module + - données +translation_of: Learn/JavaScript/Client-side_web_APIs +original_slug: Apprendre/JavaScript/Client-side_web_APIs +--- +
{{LearnSidebar}}
+ +

Lorsque vous écrivez du JavaScript côté client pour des sites Web ou des applications, vous n'irez pas très loin avant d'utiliser des API - des interfaces pour manipuler différents aspects du navigateur et du système d'exploitation sur lesquels le site opère, ou même des données provenant d'autres sites web ou services. Dans ce module, nous allons explorer ce que sont les API, et comment utiliser certaines API les plus courantes que vous rencontrerez souvent dans votre travail de développement.

+ +

Prérequis

+ +

Pour tirer le meilleur parti de ce module, vous devriez avoir parcouru les précédents modules JavaScript de la série (Premiers pas, Building blocks et objets JavaScript). Ces modules impliquent tout de même un bon nombre d'utilisations simples de l'API, car il est difficile d'écrire des exemples JavaScript côté client faisant quelque chose d'utile sans eux! Ici, nous passons à un niveau supérieur, en supposant que vous connaissiez le langage JavaScript de base et explorant les APIs Web courantes de manière un peu plus détaillée.

+ +

Une connaissance basique de HTML et CSS serait aussi utile.

+ +
+

Note : Si vous travaillez sur un ordinateur/tablette/autre périphérique où vous n'avez pas la possibilité de créer vos propres fichiers, vous pouvez essayer (la plupart) des exemples de code dans un programme de code en ligne tel que JSBin ou Thimble.

+
+ +

Guides

+ +
+
Introduction aux API du Web
+
Tout d'abord, nous survolerons du concept d'API — qu'est-ce que c'est, comment ça fonctionne, comment les utiliser dans votre code, et comment sont-elles structurées. Nous verrons également quelles sont les principales API et leur utilisation.
+
Manipuler des documents
+
Quand on écrit des pages web et des applications, une des choses les plus courantes que l'on veut faire est de manipuler la structure du document d'une manière ou d'une autre. On le fait généralement en utilisant le Document Object Model (DOM), un ensemble d'APIs qui permettent de contrôler le HTML et le style — et qui utilisent massivement l'objet {{domxref("Document")}}. Dans cet article, nous allons voir comment utiliser le DOM en détail, ainsi que quelques APIs intéressantes qui peuvent modifier votre environnement.
+
Récupérer des données du serveur
+
Une autre tâche courante dans les sites et applications web modernes est de récupérer des données à partir du serveur pour mettre à jour des sections de la page web sans la recharger entièrement. Ce qui pourrait paraître comme un petit détail, a en vérité eu un impact énorme sur les performances et le comportement des sites. Dans cet article, nous allons expliquer le concept et les technologies qui rendent cela possible, tels que {{domxref("XMLHttpRequest")}} et l'API Fetch.
+
APIs tierces
+
Les APIs que nous avons vu jusqu'à présent sont intégrées dans le navigateur, mais ce n'est pas le cas de toutes. De nombreux gros sites web tels que Google Maps, Twitter, Facebook, PayPal, etc, fournissent des APIs permettant aux développeurs d'utiliser leurs données (par exemple pour afficher un flux twitter sur un blog) ou service (par exemple afficher une carte Google Maps sur un site, ou utiliser Facebook pour permettre aux utilisateurs de se connecter). Cet article compare les APIs du navigateur aux APIs tierces et montre quelques utilisations typiques de ces dernières.
+
Dessiner des éléments graphiques
+
Le navigateur contient des outils de programmation graphique très puissants, du langage SVG (Scalable Vector Graphics), aux APIs pour dessiner sur les éléments HTML {{htmlelement("canvas")}}, (voir API Canvas et WebGL). Cet article fournit une introduction à canvas et introduit d'autres ressources pour vous permettre d'en apprendre plus.
+
APIs vidéo et audio
+
HTML5 fournit des éléments pour intégrer du multimédia dans les documents — {{htmlelement("video")}} et {{htmlelement("audio")}} — et qui viennent avec leurs propres APIs pour contrôler la lecture, se déplacer dans le flux, etcCet article montre comment réaliser les tâches les plus communes, comme créer des contrôles de lectures personnalisés.
+
Stockage côté client
+
Les navigateurs web modernes permettent aux sites web de stocker des données sur l'ordinateur de l'utilisateur — avec sa permission — puis de les récupérer au besoin. Cela permet d'enregistrer des données pour du stockage long-terme, de sauvegarder des documents ou sites hors-ligne, de conserver des préférences spécifiques à l'utilisateur, et plus encore. Cet article explique les fondamentaux pour y parvenir.
+
diff --git a/files/fr/learn/javascript/client-side_web_apis/introduction/index.html b/files/fr/learn/javascript/client-side_web_apis/introduction/index.html deleted file mode 100644 index ed8648ce6f..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/introduction/index.html +++ /dev/null @@ -1,297 +0,0 @@ ---- -title: Introduction aux API Web -slug: Learn/JavaScript/Client-side_web_APIs/Introduction -tags: - - 3rd party - - API - - Article - - Beginner - - Browser - - CodingScripting - - Learn - - Object - - WebAPI - - client-side -translation_of: Learn/JavaScript/Client-side_web_APIs/Introduction -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Introduction ---- -
{{LearnSidebar}}
- -
{{NextMenu("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs")}}
- -

Tout d'abord, nous verrons dans les grandes lignes ce qu'est une API — leur fonctionnement, comment les utiliser dans votre code, leur structure. Nous verrons également quelles sont les principales API et leur utilisation.

- - - - - - - - - - - - -
Prérequis :Des connaissances de base en informatique, une compréhension de base du HTML et CSS, des notions de JavaScript (voir premiers pas, briques JavaScript, objets JavaScript).
Objectif :Vous familiariser avec les API, ce qu'elles permettent de faire, et comment les utiliser dans votre code.
- -

Qu'est-ce qu'une API ?

- -

Les API (Application Programming Interfaces soit « interface de programmation d'application ») sont des constructions disponibles dans les langages de programmation pour permettre aux développeurs de créer plus facilement des fonctionnalités complexes. Elles s'occupent des parties de code plus complexes, fournissant au développeur une syntaxe plus facile à utiliser à la place.

- -

En guise d'exemple concret, pensez à des branchements électriques dans une maison, appartement ou autre logement. Si vous souhaitez utiliser un appareil dans votre maison, il vous suffit de le brancher dans une prise et cela fonctionne. Vous n'essayez pas de le brancher directement à l'alimentation électrique — le faire serait réellement inefficace, et, si vous n'êtes pas électricien, difficile et dangereux à réaliser.

- -

- -

Source de l'image : Overloaded plug socket par The Clear Communication People, sur Flickr.

- -

De la même façon, par exemple, pour programmer des graphismes en 3D, il est beaucoup plus facile de le faire en utilisant une API écrite dans un langage de haut niveau comme JavaScript ou Python, plutôt que d'essayer d'écrire du code bas niveau (comme C ou C ++) qui contrôle directement le GPU de l'ordinateur ou d'autres fonctions graphiques.

- -
-

Note : Voir aussi l'entrée du glossaire du terme API pour plus de descriptions.

-
- -

API JavaScript côté client

- -

Le JavaScript côté client en particulier a de nombreuses API à sa disposition — elles ne font pas partie du langage JavaScript lui-même, elles sont construites par-dessus JavaScript, offrant des super-pouvoirs supplémentaires à utiliser dans votre code. Elles appartiennent généralement à une des deux catégories :

- - - -

- -

Relations entre JavaScript, les API et autres outils JavaScript

- -

Ci-dessus, nous avons indiqué ce qu'est une API JavaScript côté client et sa relation avec le langage JavaScript. Pour récapituler, clarifier, et apporter plus de précisions sur d'autres outils JavaScript qui existent:

- - - -

Que peuvent faire les API ?

- -

Il y a un beaucoup d'API disponibles dans les navigateurs modernes. Elles permettent de faire un large éventail de choses. Vous pouvez vous en faire une petite idée en jetant un coup d'œil à la page de l'index des API MDN.

- -

API de navigateur courantes

- -

En particulier, voici les catégories d'API de navigateur les plus courantes que vous utiliserez (et que nous allons voir dans ce module plus en détail) :

- - - -

API tierces courantes

- -

Il y a une grande variété d'API tierces ; en voici quelques-unes des plus populaires que vous allez probablement utiliser tôt ou tard :

- - - -
-

Note : Vous pouvez trouver des informations sur beaucoup plus d'API tierces dans le répertoire Programmable Web API.

-
- -

Comment les API fonctionnent-elles ?

- -

Chaque API JavaScript fonctionne de manière légèrement différente d'une autre, mais de manière générale, elles ont des fonctionnalités communes et des thèmes similaires.

- -

Elles sont fondées sur des objets

- -

Les API interagissent avec le code en utilisant un ou plusieurs objets JavaScript, qui servent de conteneurs pour les données utilisées par l'API (contenues dans les propriétés d'objet), et la fonctionnalité rendue disponible par l'API (contenue dans des méthodes d'objet).

- -
-

Note : Si vous n'êtes pas déjà familier avec le fonctionnement des objets, vous devriez revenir en arrière et parcourir le module objets JavaScript avant de continuer.

-
- -

Prenons comme exemple l'API Web Audio. Il s'agit d'une API assez complexe avec plusieurs objets. Voici les objets principaux :

- - - - -

Alors comment ces objets interagissent-ils ? Si vous regardez notre exemple d'élément audio (regardez‑le aussi en direct), vous verrez le code suivant :

- -
<audio src="outfoxing.mp3"></audio>
-
-<button class="paused">Play</button>
-<br>
-<input type="range" min="0" max="1" step="0.01" value="1" class="volume">
- -

Nous incluons, tout d'abord, un élément <audio> avec lequel nous intégrons un MP3 dans la page. Nous n'incluons pas de contrôles par défaut du navigateur. Ensuite, nous incluons un <button> que nous utiliserons pour lire et arrêter la musique, et un élément <input> de type range, que nous utiliserons pour ajuster le volume de la piste en cours de lecture.

- -

Ensuite, examinons le JavaScript de cet exemple.

- -

Nous commençons par créer une instance AudioContext à l'intérieur de laquelle nous allons manipuler notre piste :

- -
const AudioContext = window.AudioContext || window.webkitAudioContext;
-const audioCtx = new AudioContext();
- -

Ensuite, nous créons des constantes qui stockent les références à nos éléments <audio>, <button> et <input>, et nous utilisons la méthode AudioContext.createMediaElementSource() pour créer un MediaElementAudioSourceNode représentant la source de notre audio — l'élément <audio> sera joué à partir de :

- -
const audioElement = document.querySelector('audio');
-const playBtn = document.querySelector('button');
-const volumeSlider = document.querySelector('.volume');
-
-const audioSource = audioCtx.createMediaElementSource(audioElement);
- -

Ensuite, nous incluons deux gestionnaires d'événements qui servent à basculer entre la lecture et la pause lorsque le bouton est pressé et à réinitialiser l'affichage au début lorsque la chanson est terminée :

- -
// lecture/pause de l'audio
-playBtn.addEventListener('click', function() {
-    // vérifier si le contexte est en état de suspension (politique de lecture automatique)
-    if (audioCtx.state === 'suspended') {
-        audioCtx.resume();
-    }
-
-  // si la piste est arrêtée, la lire
-    if (this.getAttribute('class') === 'paused') {
-        audioElement.play();
-        this.setAttribute('class', 'playing');
-        this.textContent = 'Pause'
-    // si une piste est en cours de lecture, l'arrêter
-} else if (this.getAttribute('class') === 'playing') {
-        audioElement.pause();
-        this.setAttribute('class', 'paused');
-        this.textContent = 'Lire';
-    }
-});
-
-// si la piste se termine
-audioElement.addEventListener('ended', function() {
-    playBtn.setAttribute('class', 'paused');
-    playBtn.textContent = 'Lire';
-});
- -
-

Note : Certains d'entre vous ont peut-être remarqué que les méthodes play() et pause() utilisées pour lire et mettre en pause la piste ne font pas partie de l'API audio Web ; elles font partie de l'API HTMLMediaElement. qui est différente mais étroitement liée.

-
- -

Ensuite, nous créons un objet GainNode à l'aide de la méthode AudioContext.createGain(), qui peut être utilisé pour ajuster le volume de l'audio qui le traverse, et nous créons un autre gestionnaire d'événements qui modifie la valeur du gain (volume) du graphique audio lorsque la valeur du curseur est modifiée :

- -
const gainNode = audioCtx.createGain();
-
-volumeSlider.addEventListener('input', function() {
-    gainNode.gain.value = this.value;
-});
- -

La dernière chose à faire pour que cela fonctionne est de connecter les différents nœuds du graphe audio, ce qui est fait en utilisant la méthode AudioNode.connect() disponible sur chaque type de nœud :

- -
audioSource.connect(gainNode).connect(audioCtx.destination);
- -

L'audio commence dans la source, qui est ensuite connectée au nœud de gain afin que le volume de l'audio puisse être ajusté. Le nœud de gain est ensuite connecté au nœud de destination afin que le son puisse être lu sur votre ordinateur (la propriété AudioContext.destination représente ce qui est le AudioDestinationNode par défaut disponible sur le matériel de votre ordinateur, par exemple vos haut-parleurs).

- -

Elles ont des points d'entrée reconnaissables

- -

Lorsque vous utilisez une API, vous devez vous assurer que vous savez où se trouve le point d'entrée de l'API. Dans l'API Web Audio, c'est assez simple — il s'agit de l'objet AudioContext, qui doit être utilisé pour effectuer toute manipulation audio quelle qu'elle soit.

- -

L'API DOM (Document Object Model) a également un point d'entrée simple — ses fonctionnalités ont tendance à être trouvées accrochées à l'objet Document, ou à une instance d'un élément HTML que vous voulez affecter d'une manière ou d'une autre, par exemple :

- -
const em = document.createElement('em'); // crée un nouvel élément em
-const para = document.querySelector('p'); // référence à un élément p existant
-em.textContent = 'Hello there!'; // donne à em du contenu textuel
-para.appendChild(em); // intégre em dans le paragraphe
- -

L'API Canvas repose également sur l'obtention d'un objet de contexte à utiliser pour manipuler les choses, bien que dans ce cas, il s'agisse d'un contexte graphique plutôt que d'un contexte audio. Son objet de contexte est créé en obtenant une référence à l'élément <canvas> sur lequel vous voulez dessiner, puis en appelant sa méthode HTMLCanvasElement.getContext() :

- -
const canvas = document.querySelector('canvas');
-const ctx = canvas.getContext('2d');
- -

Tout ce que nous voulons faire au canevas est ensuite réalisé en appelant les propriétés et les méthodes de l'objet contexte (qui est une instance de CanvasRenderingContext2D), par exemple :

- -
Ball.prototype.draw = function() {
-  ctx.beginPath();
-  ctx.fillStyle = this.color;
-  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
-  ctx.fill();
-};
- -
-

Note : Vous pouvez voir ce code en action dans notre démo de balles rebondissantes (voyez-le fonctionner en direct également).

-
- -

Elles utilisent des événements pour gérer les changements d'état

- -

Nous avons déjà abordé les événements plus tôt dans le cours dans notre article Introduction aux événements, qui examine en détail ce que sont les événements web côté client et comment ils sont utilisés dans votre code. Si vous n'êtes pas déjà familiarisé avec le fonctionnement des événements de l'API Web côté client, nous vous conseillons de lire cet article avant de poursuivre.

- -

Certaines API Web ne contiennent aucun événement, mais la plupart en contiennent au moins quelques-uns. Les propriétés des gestionnaires qui nous permettent d'exécuter des fonctions lorsque des événements se produisent sont généralement répertoriées dans notre matériel de référence dans des sections distinctes intitulées « Gestionnaires d'événements ».

- -

Nous avons déjà vu un certain nombre de gestionnaires d'événements utilisés dans notre exemple d'API audio Web ci-dessus.

- -

Pour fournir un autre exemple, les instances de l'objet XMLHttpRequest (chacune représente une requête HTTP au serveur pour récupérer une nouvelle ressource d'un certain type) a un certain nombre d'événements disponibles sur eux, par exemple, l'événement load est déclenché lorsqu'une réponse a été renvoyée avec succès contenant la ressource demandée, et qu'elle est désormais disponible.

- -

Le code suivant fournit un exemple simple de la façon dont cela peut être utilisé :

- -
let requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
-let request = new XMLHttpRequest();
-request.open('GET', requestURL);
-request.responseType = 'json';
-request.send();
-
-request.onload = function() {
-  const superHeroes = request.response;
-  populateHeader(superHeroes);
-  showHeroes(superHeroes);
-}
- -
-

Note : Vous pouvez voir ce code en action dans notre exemple ajax.html (voyez-le en direct également).

-
- -

Les cinq premières lignes spécifient l'emplacement de la ressource que nous voulons récupérer, créent une nouvelle instance d'un objet de requête en utilisant le constructeur XMLHttpRequest(), ouvrent une requête HTTP GET pour récupérer la ressource spécifiée, spécifient que la réponse doit être envoyée au format JSON, puis envoient la requête.

- -

La fonction du gestionnaire onload précise ensuite ce que nous faisons de la réponse. Nous savons que la réponse sera renvoyée avec succès et disponible après le déclenchement de l'événement load (sauf si une erreur s'est produite), nous sauvegardons donc la réponse contenant le JSON renvoyé dans la variable superHeroes, puis nous la passons à deux fonctions différentes pour un traitement ultérieur.

- -

Elles disposent de mécanismes de sécurité supplémentaires adéquats

- -

Les fonctionnalités des API Web sont soumises aux mêmes considérations de sécurité que JavaScript et les autres technologies web (par exemple same-origin policy), mais elles disposent parfois de mécanismes de sécurité supplémentaires. Par exemple, certaines des API Web les plus modernes ne fonctionneront que sur des pages servies par HTTPS, car elles transmettent des données potentiellement sensibles (par exemple Service Workers et Push).

- -

En outre, certaines API Web demandent la permission d'être activées à l'utilisateur une fois que les appels à ces interfaces sont effectués dans votre code. À titre d'exemple, l'API Notifications API demande la permission à l'aide d'une boîte de dialogue contextuelle :

- -

- -

Les API Web Audio et HTMLMediaElement sont soumises à un mécanisme de sécurité appelé autoplay policy - cela signifie essentiellement que vous ne pouvez pas lire automatiquement l'audio lorsqu'une page se charge — vous devez permettre à vos utilisateurs de déclencher la lecture audio par le biais d'un contrôle comme un bouton. Cette mesure est prise parce que la lecture automatique de l'audio est généralement très ennuyeuse et que nous ne devrions pas y soumettre nos utilisateurs.

- -
-

Note : Selon la rigueur du navigateur, ces mécanismes de sécurité peuvent même empêcher l'exemple de fonctionner localement, c'est-à-dire si vous chargez le fichier d'exemple local dans votre navigateur au lieu de l'exécuter à partir d'un serveur web. Au moment de la rédaction de ce document, notre exemple d'API Web Audio ne fonctionnait pas localement sur Google Chrome — nous avons dû le télécharger sur GitHub avant qu'il ne fonctionne.

-
- -

Résumé

- -

À ce stade, vous devriez avoir une bonne idée de ce que sont les API, de leur fonctionnement et de ce que vous pouvez faire avec dans votre code JavaScript. Vous avec probablement hâte de commencer à faire des choses amusantes avec des API spécifiques, alors allons-y ! Par la suite, nous verrons comment manipuler des documents avec le Document Object Model (DOM).

- -

{{NextMenu("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/javascript/client-side_web_apis/introduction/index.md b/files/fr/learn/javascript/client-side_web_apis/introduction/index.md new file mode 100644 index 0000000000..ed8648ce6f --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/introduction/index.md @@ -0,0 +1,297 @@ +--- +title: Introduction aux API Web +slug: Learn/JavaScript/Client-side_web_APIs/Introduction +tags: + - 3rd party + - API + - Article + - Beginner + - Browser + - CodingScripting + - Learn + - Object + - WebAPI + - client-side +translation_of: Learn/JavaScript/Client-side_web_APIs/Introduction +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

Tout d'abord, nous verrons dans les grandes lignes ce qu'est une API — leur fonctionnement, comment les utiliser dans votre code, leur structure. Nous verrons également quelles sont les principales API et leur utilisation.

+ + + + + + + + + + + + +
Prérequis :Des connaissances de base en informatique, une compréhension de base du HTML et CSS, des notions de JavaScript (voir premiers pas, briques JavaScript, objets JavaScript).
Objectif :Vous familiariser avec les API, ce qu'elles permettent de faire, et comment les utiliser dans votre code.
+ +

Qu'est-ce qu'une API ?

+ +

Les API (Application Programming Interfaces soit « interface de programmation d'application ») sont des constructions disponibles dans les langages de programmation pour permettre aux développeurs de créer plus facilement des fonctionnalités complexes. Elles s'occupent des parties de code plus complexes, fournissant au développeur une syntaxe plus facile à utiliser à la place.

+ +

En guise d'exemple concret, pensez à des branchements électriques dans une maison, appartement ou autre logement. Si vous souhaitez utiliser un appareil dans votre maison, il vous suffit de le brancher dans une prise et cela fonctionne. Vous n'essayez pas de le brancher directement à l'alimentation électrique — le faire serait réellement inefficace, et, si vous n'êtes pas électricien, difficile et dangereux à réaliser.

+ +

+ +

Source de l'image : Overloaded plug socket par The Clear Communication People, sur Flickr.

+ +

De la même façon, par exemple, pour programmer des graphismes en 3D, il est beaucoup plus facile de le faire en utilisant une API écrite dans un langage de haut niveau comme JavaScript ou Python, plutôt que d'essayer d'écrire du code bas niveau (comme C ou C ++) qui contrôle directement le GPU de l'ordinateur ou d'autres fonctions graphiques.

+ +
+

Note : Voir aussi l'entrée du glossaire du terme API pour plus de descriptions.

+
+ +

API JavaScript côté client

+ +

Le JavaScript côté client en particulier a de nombreuses API à sa disposition — elles ne font pas partie du langage JavaScript lui-même, elles sont construites par-dessus JavaScript, offrant des super-pouvoirs supplémentaires à utiliser dans votre code. Elles appartiennent généralement à une des deux catégories :

+ + + +

+ +

Relations entre JavaScript, les API et autres outils JavaScript

+ +

Ci-dessus, nous avons indiqué ce qu'est une API JavaScript côté client et sa relation avec le langage JavaScript. Pour récapituler, clarifier, et apporter plus de précisions sur d'autres outils JavaScript qui existent:

+ + + +

Que peuvent faire les API ?

+ +

Il y a un beaucoup d'API disponibles dans les navigateurs modernes. Elles permettent de faire un large éventail de choses. Vous pouvez vous en faire une petite idée en jetant un coup d'œil à la page de l'index des API MDN.

+ +

API de navigateur courantes

+ +

En particulier, voici les catégories d'API de navigateur les plus courantes que vous utiliserez (et que nous allons voir dans ce module plus en détail) :

+ + + +

API tierces courantes

+ +

Il y a une grande variété d'API tierces ; en voici quelques-unes des plus populaires que vous allez probablement utiliser tôt ou tard :

+ + + +
+

Note : Vous pouvez trouver des informations sur beaucoup plus d'API tierces dans le répertoire Programmable Web API.

+
+ +

Comment les API fonctionnent-elles ?

+ +

Chaque API JavaScript fonctionne de manière légèrement différente d'une autre, mais de manière générale, elles ont des fonctionnalités communes et des thèmes similaires.

+ +

Elles sont fondées sur des objets

+ +

Les API interagissent avec le code en utilisant un ou plusieurs objets JavaScript, qui servent de conteneurs pour les données utilisées par l'API (contenues dans les propriétés d'objet), et la fonctionnalité rendue disponible par l'API (contenue dans des méthodes d'objet).

+ +
+

Note : Si vous n'êtes pas déjà familier avec le fonctionnement des objets, vous devriez revenir en arrière et parcourir le module objets JavaScript avant de continuer.

+
+ +

Prenons comme exemple l'API Web Audio. Il s'agit d'une API assez complexe avec plusieurs objets. Voici les objets principaux :

+ + + + +

Alors comment ces objets interagissent-ils ? Si vous regardez notre exemple d'élément audio (regardez‑le aussi en direct), vous verrez le code suivant :

+ +
<audio src="outfoxing.mp3"></audio>
+
+<button class="paused">Play</button>
+<br>
+<input type="range" min="0" max="1" step="0.01" value="1" class="volume">
+ +

Nous incluons, tout d'abord, un élément <audio> avec lequel nous intégrons un MP3 dans la page. Nous n'incluons pas de contrôles par défaut du navigateur. Ensuite, nous incluons un <button> que nous utiliserons pour lire et arrêter la musique, et un élément <input> de type range, que nous utiliserons pour ajuster le volume de la piste en cours de lecture.

+ +

Ensuite, examinons le JavaScript de cet exemple.

+ +

Nous commençons par créer une instance AudioContext à l'intérieur de laquelle nous allons manipuler notre piste :

+ +
const AudioContext = window.AudioContext || window.webkitAudioContext;
+const audioCtx = new AudioContext();
+ +

Ensuite, nous créons des constantes qui stockent les références à nos éléments <audio>, <button> et <input>, et nous utilisons la méthode AudioContext.createMediaElementSource() pour créer un MediaElementAudioSourceNode représentant la source de notre audio — l'élément <audio> sera joué à partir de :

+ +
const audioElement = document.querySelector('audio');
+const playBtn = document.querySelector('button');
+const volumeSlider = document.querySelector('.volume');
+
+const audioSource = audioCtx.createMediaElementSource(audioElement);
+ +

Ensuite, nous incluons deux gestionnaires d'événements qui servent à basculer entre la lecture et la pause lorsque le bouton est pressé et à réinitialiser l'affichage au début lorsque la chanson est terminée :

+ +
// lecture/pause de l'audio
+playBtn.addEventListener('click', function() {
+    // vérifier si le contexte est en état de suspension (politique de lecture automatique)
+    if (audioCtx.state === 'suspended') {
+        audioCtx.resume();
+    }
+
+  // si la piste est arrêtée, la lire
+    if (this.getAttribute('class') === 'paused') {
+        audioElement.play();
+        this.setAttribute('class', 'playing');
+        this.textContent = 'Pause'
+    // si une piste est en cours de lecture, l'arrêter
+} else if (this.getAttribute('class') === 'playing') {
+        audioElement.pause();
+        this.setAttribute('class', 'paused');
+        this.textContent = 'Lire';
+    }
+});
+
+// si la piste se termine
+audioElement.addEventListener('ended', function() {
+    playBtn.setAttribute('class', 'paused');
+    playBtn.textContent = 'Lire';
+});
+ +
+

Note : Certains d'entre vous ont peut-être remarqué que les méthodes play() et pause() utilisées pour lire et mettre en pause la piste ne font pas partie de l'API audio Web ; elles font partie de l'API HTMLMediaElement. qui est différente mais étroitement liée.

+
+ +

Ensuite, nous créons un objet GainNode à l'aide de la méthode AudioContext.createGain(), qui peut être utilisé pour ajuster le volume de l'audio qui le traverse, et nous créons un autre gestionnaire d'événements qui modifie la valeur du gain (volume) du graphique audio lorsque la valeur du curseur est modifiée :

+ +
const gainNode = audioCtx.createGain();
+
+volumeSlider.addEventListener('input', function() {
+    gainNode.gain.value = this.value;
+});
+ +

La dernière chose à faire pour que cela fonctionne est de connecter les différents nœuds du graphe audio, ce qui est fait en utilisant la méthode AudioNode.connect() disponible sur chaque type de nœud :

+ +
audioSource.connect(gainNode).connect(audioCtx.destination);
+ +

L'audio commence dans la source, qui est ensuite connectée au nœud de gain afin que le volume de l'audio puisse être ajusté. Le nœud de gain est ensuite connecté au nœud de destination afin que le son puisse être lu sur votre ordinateur (la propriété AudioContext.destination représente ce qui est le AudioDestinationNode par défaut disponible sur le matériel de votre ordinateur, par exemple vos haut-parleurs).

+ +

Elles ont des points d'entrée reconnaissables

+ +

Lorsque vous utilisez une API, vous devez vous assurer que vous savez où se trouve le point d'entrée de l'API. Dans l'API Web Audio, c'est assez simple — il s'agit de l'objet AudioContext, qui doit être utilisé pour effectuer toute manipulation audio quelle qu'elle soit.

+ +

L'API DOM (Document Object Model) a également un point d'entrée simple — ses fonctionnalités ont tendance à être trouvées accrochées à l'objet Document, ou à une instance d'un élément HTML que vous voulez affecter d'une manière ou d'une autre, par exemple :

+ +
const em = document.createElement('em'); // crée un nouvel élément em
+const para = document.querySelector('p'); // référence à un élément p existant
+em.textContent = 'Hello there!'; // donne à em du contenu textuel
+para.appendChild(em); // intégre em dans le paragraphe
+ +

L'API Canvas repose également sur l'obtention d'un objet de contexte à utiliser pour manipuler les choses, bien que dans ce cas, il s'agisse d'un contexte graphique plutôt que d'un contexte audio. Son objet de contexte est créé en obtenant une référence à l'élément <canvas> sur lequel vous voulez dessiner, puis en appelant sa méthode HTMLCanvasElement.getContext() :

+ +
const canvas = document.querySelector('canvas');
+const ctx = canvas.getContext('2d');
+ +

Tout ce que nous voulons faire au canevas est ensuite réalisé en appelant les propriétés et les méthodes de l'objet contexte (qui est une instance de CanvasRenderingContext2D), par exemple :

+ +
Ball.prototype.draw = function() {
+  ctx.beginPath();
+  ctx.fillStyle = this.color;
+  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+  ctx.fill();
+};
+ +
+

Note : Vous pouvez voir ce code en action dans notre démo de balles rebondissantes (voyez-le fonctionner en direct également).

+
+ +

Elles utilisent des événements pour gérer les changements d'état

+ +

Nous avons déjà abordé les événements plus tôt dans le cours dans notre article Introduction aux événements, qui examine en détail ce que sont les événements web côté client et comment ils sont utilisés dans votre code. Si vous n'êtes pas déjà familiarisé avec le fonctionnement des événements de l'API Web côté client, nous vous conseillons de lire cet article avant de poursuivre.

+ +

Certaines API Web ne contiennent aucun événement, mais la plupart en contiennent au moins quelques-uns. Les propriétés des gestionnaires qui nous permettent d'exécuter des fonctions lorsque des événements se produisent sont généralement répertoriées dans notre matériel de référence dans des sections distinctes intitulées « Gestionnaires d'événements ».

+ +

Nous avons déjà vu un certain nombre de gestionnaires d'événements utilisés dans notre exemple d'API audio Web ci-dessus.

+ +

Pour fournir un autre exemple, les instances de l'objet XMLHttpRequest (chacune représente une requête HTTP au serveur pour récupérer une nouvelle ressource d'un certain type) a un certain nombre d'événements disponibles sur eux, par exemple, l'événement load est déclenché lorsqu'une réponse a été renvoyée avec succès contenant la ressource demandée, et qu'elle est désormais disponible.

+ +

Le code suivant fournit un exemple simple de la façon dont cela peut être utilisé :

+ +
let requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
+let request = new XMLHttpRequest();
+request.open('GET', requestURL);
+request.responseType = 'json';
+request.send();
+
+request.onload = function() {
+  const superHeroes = request.response;
+  populateHeader(superHeroes);
+  showHeroes(superHeroes);
+}
+ +
+

Note : Vous pouvez voir ce code en action dans notre exemple ajax.html (voyez-le en direct également).

+
+ +

Les cinq premières lignes spécifient l'emplacement de la ressource que nous voulons récupérer, créent une nouvelle instance d'un objet de requête en utilisant le constructeur XMLHttpRequest(), ouvrent une requête HTTP GET pour récupérer la ressource spécifiée, spécifient que la réponse doit être envoyée au format JSON, puis envoient la requête.

+ +

La fonction du gestionnaire onload précise ensuite ce que nous faisons de la réponse. Nous savons que la réponse sera renvoyée avec succès et disponible après le déclenchement de l'événement load (sauf si une erreur s'est produite), nous sauvegardons donc la réponse contenant le JSON renvoyé dans la variable superHeroes, puis nous la passons à deux fonctions différentes pour un traitement ultérieur.

+ +

Elles disposent de mécanismes de sécurité supplémentaires adéquats

+ +

Les fonctionnalités des API Web sont soumises aux mêmes considérations de sécurité que JavaScript et les autres technologies web (par exemple same-origin policy), mais elles disposent parfois de mécanismes de sécurité supplémentaires. Par exemple, certaines des API Web les plus modernes ne fonctionneront que sur des pages servies par HTTPS, car elles transmettent des données potentiellement sensibles (par exemple Service Workers et Push).

+ +

En outre, certaines API Web demandent la permission d'être activées à l'utilisateur une fois que les appels à ces interfaces sont effectués dans votre code. À titre d'exemple, l'API Notifications API demande la permission à l'aide d'une boîte de dialogue contextuelle :

+ +

+ +

Les API Web Audio et HTMLMediaElement sont soumises à un mécanisme de sécurité appelé autoplay policy - cela signifie essentiellement que vous ne pouvez pas lire automatiquement l'audio lorsqu'une page se charge — vous devez permettre à vos utilisateurs de déclencher la lecture audio par le biais d'un contrôle comme un bouton. Cette mesure est prise parce que la lecture automatique de l'audio est généralement très ennuyeuse et que nous ne devrions pas y soumettre nos utilisateurs.

+ +
+

Note : Selon la rigueur du navigateur, ces mécanismes de sécurité peuvent même empêcher l'exemple de fonctionner localement, c'est-à-dire si vous chargez le fichier d'exemple local dans votre navigateur au lieu de l'exécuter à partir d'un serveur web. Au moment de la rédaction de ce document, notre exemple d'API Web Audio ne fonctionnait pas localement sur Google Chrome — nous avons dû le télécharger sur GitHub avant qu'il ne fonctionne.

+
+ +

Résumé

+ +

À ce stade, vous devriez avoir une bonne idée de ce que sont les API, de leur fonctionnement et de ce que vous pouvez faire avec dans votre code JavaScript. Vous avec probablement hâte de commencer à faire des choses amusantes avec des API spécifiques, alors allons-y ! Par la suite, nous verrons comment manipuler des documents avec le Document Object Model (DOM).

+ +

{{NextMenu("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.html b/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.html deleted file mode 100644 index 8025213ac4..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.html +++ /dev/null @@ -1,332 +0,0 @@ ---- -title: Manipuler des documents -slug: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents -tags: - - API - - Apprendre - - Article - - Codage - - DOM - - Document Object Model - - Débutant - - JavaScript - - Navigator - - WebAPI - - Window -translation_of: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Manipulating_documents ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}
- -

Quand on écrit des pages web et des applications, une des choses les plus courantes que l'on veut faire est de manipuler la structure du document d'une manière ou d'une autre. On le fait généralement en utilisant le Document Object Model (DOM), un ensemble d'APIs qui permettent de contrôler le HTML et le style — et qui utilisent massivement l'objet Document. Dans cet article, nous allons voir comment utiliser le DOM en détail, ainsi que quelques APIs intéressantes qui peuvent modifier votre environnement.

- - - - - - - - - - - - -
Prérequis :Connaissances de base en informatique, notions de base d'HTML, CSS, et JavaScript — dont les objets JavaScript.
Objectif :Se familiariser avec les APIs DOM, et autres APIs souvent associées au DOM et à la manipulation de document
- -

Les principaux composants du navigateur

- -

Les navigateurs web sont des logiciels très complexes avec beaucoup de composants, dont beaucoup ne peuvent pas être contrôlés ou manipulés en utilisant JavaScript. Vous pourriez penser que de telles limitations sont une mauvaise chose, mais les navigateurs sont verrouillés pour de bonnes raisons, la plupart du temps pour des raisons de sécurité. Imaginez qu'un site web puisse accéder à vos mots de passe stockés ou à d'autres informations sensibles, ou se connecter à des sites web comme si c'était vous?

- -

Malgré les limitations, les APIs Web nous donnent accès à beaucoup de fonctionnalités, lesquelles nous permettent de faire plein de choses géniales avec les pages web. Il existe quelques éléments évidents que vous utilisez régulièrement dans votre code — jetez un coup d'œil au diagramme suivant, il représente les principaux composants du navigateur directement impliqués dans l'affichage des pages web:

- -

- - - -

Dans cet article, nous allons principalement nous concentrer sur la manipulation du document, mais nous verrons également quelques autres éléments utiles.

- -

Le modèle objet du document (Document Object Model)

- -

Le document chargé dans chaque onglet de votre navigateur, et donc son contenu, est accessible via un modèle objet du document — Document Objet Model en anglais, ou DOM. Il s'agit d'une structure arborescente créée par le navigateur et qui permet aux langages de programmation d'accéder facilement à la structure HTML — par exemple, le navigateur lui-même l'utilise pour appliquer différents styles aux éléments correspondants sur la page, tandis qu'un développeur comme vous et moi peut l'utiliser pour manipuler le DOM avec du JavaScript après que la page ait été chargée.

- -

Nous avons créé une simple page d'exemple, dom-example.html (voir en direct). Essayez de l'ouvrir dans votre navigateur — c'est une page très simple qui contient un élément <section>, à l'intérieur duquel se trouve une image et un paragraphe avec un lien. Le code source HTML ressemble à ça:

- -
<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>Simple DOM example</title>
-  </head>
-  <body>
-      <section>
-        <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
-        <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
-      </section>
-  </body>
-</html>
- -

Le DOM, quant à lui, ressemble à ça :

- -

- -
-

Note : Ce diagramme du DOM a été créé en utilisant le Live DOM viewer de Ian Hickson.

-
- -

Vous pouvez voir ici que chaque élément et morceau de texte dans le document possède sa propre entrée dans l'arbre — chacune étant appelée nœud (node). Vous rencontrerez également plusieurs termes pour décrire les différents type de nœuds ou leur position dans l'arbre les uns par rapport aux autres :

- - - -

Il est utile de vous familiariser avec ces termes avant de travailler avec le DOM, puisqu'un certain nombre de documentations les utilisent. Vous les avez peut-être déjà rencontrés si vous avez étudié le CSS (ex. sélecteur descendant, sélecteur enfant).

- -

Apprentissage actif : Manipulations basiques du DOM

- -

Pour commencer l'apprentissage de la manipulation du DOM, commençons par un exemple concret.

- -
    -
  1. Faites une copie locale de la page dom-example.html et de l'image qui l'accompagne.
  2. -
  3. Ajoutez un élément <script></script> juste avant la balise fermante </body>.
  4. -
  5. Pour manipuler un élément dans le DOM, vous allez d'abord sélectionner cet élément et stocker une référence à cet élément dans une variable. À l'intérieur de votre élément <script>, ajoutez la ligne suivante: -
    const link = document.querySelector('a');
    -
  6. -
  7. Maintenant que nous avons la référence à l'élément enregistrée dans une variable, nous pouvons commencer à le manipuler en utilisant les propriétés et les méthodes qui lui sont associées (celles-ci sont définies sur les interfaces telles que HTMLAnchorElement dans le cas d'un élément <a>, et sur les interfaces plus génériques HTMLElement, et Node — qui représente tous les nœuds d'un DOM). Tout d'abord, changeons le texte du lien en mettant à jour la valeur de la propriété Node.textContent. Ajoutez la ligne suivante à la suite de la précédente : -
    link.textContent = 'Mozilla Developer Network';
    -
  8. -
  9. Nous devons également modifier l'URL ciblée par le lien, pour qu'il ne renvoie pas au mauvais endroit quand on clique dessus. Ajoutez la ligne suivante, en bas de votre JavaScript : -
    link.href = 'https://developer.mozilla.org';
    -
  10. -
- -
-

Notez que, comme souvent en JavaScript, il y a plusieurs façons de sélectionner et d'enregistrer une référence à un élément dans une variable. Document.querySelector() est l'approche moderne recommandée — elle est pratique puisqu'elle permet de sélectionner des éléments en utilisant les sélecteurs CSS. L'appel à querySelector() que nous avons utilisé plus tôt récupère le premier élément <a> qui apparaît dans le document. Si vous souhaitez au contraire récupérer plusieurs éléments, vous pouvez utiliser Document.querySelectorAll(), qui récupère tous les éléments du document correspondant au sélecteur, et retourne des références vers ces éléments dans un objet similaire à un tableau appelé un NodeList.

- -

Il existe des méthodes plus anciennes pour récupérer des références aux éléments, telles que :

- - - -

Ces deux dernières méthodes fonctionnent mieux dans les navigateurs plus anciens que des méthodes plus modernes comme querySelector(), mais elles sont beaucoup moins pratiques. Regardez autour de vous et essayez d'en trouver d'autres!

-
- -

Créer et placer de nouveaux nœuds

- -

Ce qui précède vous a donné un petit avant-goût de ce que vous pouvez faire, mais allons plus loin et regardons comment créer de nouveaux éléments.

- -
    -
  1. Pour revenir à notre exemple, commençons par récupérer une référence à notre élément <section> — ajoutez le code suivant au bas de votre script existant (idem avec les lignes qui suivront) : -
    var sect = document.querySelector('section');
    -
  2. -
  3. Nous allons maintenant créer un nouveau paragraphe grâce à Document.createElement(), et lui donner du contenu texte de la même manière que précédemment : -
    var para = document.createElement('p');
    -para.textContent = 'We hope you enjoyed the ride.';
    -
  4. -
  5. Nous pouvons à présent ajouter ce paragraphe au bas de la section en utilisant Node.appendChild(): -
    sect.appendChild(para);
    -
  6. -
  7. Enfin, ajoutons un nœud texte au premier paragraphe, pour finir la phrase joliment. Créons d'abord un nœud texte avec Document.createTextNode() : -
    var text = document.createTextNode(' — the premier source for web development knowledge.');
    -
  8. -
  9. Puis, après avoir récupéré une référence au premier paragraphe, ajoutons-y le nœud texte: -
    var linkPara = document.querySelector('p');
    -linkPara.appendChild(text);
    -
  10. -
- -

C'est l'essentiel de ce dont vous aurez besoin pour ajouter des nœuds au DOM — vous utiliserez beaucoup ces méthodes lorsque vous construirez des interfaces dynamiques (nous en verrons quelques exemples plus tard).

- -

Déplacer et supprimer des éléments

- -

Il peut arriver qu'on veuille déplacer des nœuds, ou même les supprimer du DOM. C'est tout à fait possible, et voyons comment.

- - -

Par exemple, si l'on veut déplacer le premier paragraphe de notre exemple au bas de la section, on pourrait utiliser :

- -
sect.appendChild(linkPara);
- -

Cette commande va déplacer le paragraphe tout au bas de la section. On pourrait penser qu'elle va en fait ajouter une copie, mais ce n'est pas le cas : linkPara ne fait référence qu'à un seul et unique élément. Pour copier le paragraphe, il faudrait utiliser Node.cloneNode() à la place.

- -

Supprimer des éléments est également plutôt simple, dès lors qu'on a une référence de l'élément à supprimer et de son parent. Dans notre cas, on peut simplement utiliser Node.removeChild(), comme ceci :

- -
sect.removeChild(linkPara);
- -

Si vous souhaitez un élément uniquement à partir d'une référence à cet élément, comme c'est souvent le cas, vous pouvez utiliser ChildNode.remove() :

- -
linkPara.remove();
- -

Cette méthode ne fonctionne cependant pas dans les navigateurs plus anciens. Ils ne possèdent en effet pas de méthodes pour dire à un nœud de se supprimer, et il faut donc procéder comme suit :

- -
linkPara.parentNode.removeChild(linkPara);
- -

À votre tour : essayez les lignes ci-dessus en les ajoutant à votre code.

- -

Manipuler le style

- -

Il est possible de manipuler des styles CSS grâce à du JavaScript de plusieurs manières.

- -

Pour commencer, vous pouvez obtenir une liste de toutes les feuilles de style associées à un document en utilisant Document.stylesheets, qui retourne un objet, ressemblant à un tableau composé d'objets CSSStyleSheet. Vous pouvez alors ajouter/supprimer des styles comme vous le souhaitez. Cependant, nous n'allons pas nous étendre sur ces fonctionnalités, car elles sont archaïques et il est difficile de manipuler le style avec. Il y a des techniques beaucoup plus simples.

- -

La première d'entre elles consiste à ajouter des styles en ligne (inline styles), directement sur les éléments que vous voulez styliser de façon dynamique. Pour ce faire, on utilise la propriété HTMLElement.style, qui contient les informations de style en ligne de chaque élément du document. Vous pouvez définir des propriétés de cet objet de façon à pouvoir mettre à jour directement le style des éléments.

- -
    -
  1. À titre d'exemple, essayez d'ajouter les lignes suivantes à notre exemple : -
    para.style.color = 'white';
    -para.style.backgroundColor = 'black';
    -para.style.padding = '10px';
    -para.style.width = '250px';
    -para.style.textAlign = 'center';
    -
  2. -
  3. Rafraichissez la page, et vous verrez que les styles ont été appliqués au paragraphe. Si vous regardez ce paragraphe dans l'Inspecteur du navigateur, vous verrez que ces lignes sont en effet ajoutées comme du style en ligne au document: -
    <p style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;">We hope you enjoyed the ride.</p>
    -
  4. -
- -
-

Note : Vous remarquerez que les propriétés JavaScript qui représentent les propriétés CSS sont écrites en lower camel case tandis que les versions CSS sont reliées par des tirets (par exemple backgroundColor au lieu de background-color). Prenez garde à ne pas les mélanger, sans quoi ça ne fonctionnera pas.

-
- -

Il y a un autre moyen de manipuler dynamiquement des styles sur votre document, que nous allons étudier maintenant.

- -
    -
  1. Supprimez les cinq lignes précédentes que nous avons ajoutées à notre code JavaScript.
  2. -
  3. Ajoutez ce qui suit au sein de la balise <head> de votre HTML: -
    <style>
    -.highlight {
    -  color: white;
    -  background-color: black;
    -  padding: 10px;
    -  width: 250px;
    -  text-align: center;
    -}
    -</style>
    -
  4. -
  5. Nous allons maintenant utiliser une méthode très utile pour la manipulation HTML de manière générale : Element.setAttribute(). Cette fonction prend deux paramètres : le nom de l'attribut que vous voulez définir sur l'élément, et la valeur que vous voulez lui attribuer. Ici nous allons ajouter une classe highlight à notre élément : -
    para.setAttribute('class', 'highlight');
    -
  6. -
  7. Rafraîchissez votre page, et vous constaterez qu'il n'y a aucun changement par rapport au dernier exemple. Le CSS est toujours appliqué au paragraphe, mais la seule différence c'est qu'on a utilisé une classe pour le faire et non des styles en ligne.
  8. -
- -

A vous de choisir la méthode que vous souhaitez utiliser : chacune a ses avantages et ses inconvénients. Les styles en ligne demandent moins de préparation et sont utiles pour un usage simple, tandis que l'usage des classes est une méthode plus pure (on ne mélange pas le CSS et le JavaScript, on évite donc les styles en ligne, car c'est considéré comme une mauvaise pratique). Au fur et à mesure que vous construirez des applications plus volumineuses et complexes, vous allez probablement utiliser la deuxième méthode plus souvent, mais c'est à vous de décider.

- -

À ce stade, nous n'avons pas vraiment fait quoi que soit d'utile! Il n'y a pas d'intérêt à générer du contenu statique avec du JavaScript — autant l'écrire directement en HTML sans passer par du JavaScript. Le JavaScript est plus complexe que du HTML, et comporte son propre lot de problèmes (comme le fait qu'il ne puisse pas être lu par les moteurs de recherche).

- -

Dans les deux prochaines sections, nous verrons des exemples d'utilisation plus pratiques des APIs du DOM.

- -
-

Note : Vous pouvez trouver la version finale de dom-example.html sur GitHub (le voir en direct aussi).

-
- -

Apprentissage actif : Récupérer des informations utiles depuis l'objet Window

- -

Jusqu'à présent nous avons seulement utilisé les fonctionnalités de Node et Document (le DOM) pour manipuler les documents, mais vous pouvez obtenir des données d'autres sources pour les intégrer à votre interface utilisateur (UI). Il faut simplement s'assurer que les données sont au bon format ; c'est plus simple avec JavaScript qu'avec d'autres langages, puisqu'on utilise un typage faible — les nombres par exemple sont automatiquement convertis en texte lorsqu'on les affiche à l'écran.

- -

Dans cet exemple, nous allons résoudre un problème très courant — s'assurer que votre application est de la même taille que la fenêtre, quelle que soit la taille de la fenêtre. C'est souvent utile pour des jeux par exemple, où l'on veut utiliser autant d'espace d'écran que possible pour jouer.

- -

Pour commencer, faites une copie en local des fichiers de démonstration window-resize-example.html et bgtile.png. Ouvrez-les : vous y trouverez un élément <div> qui couvre une petite partie de l'écran avec un motif en mosaïque. Nous utiliserons cet élément pour représenter la surface de notre interface utilisateur.

- -
    -
  1. Tout d'abord, nous allons récupérer : une référence au <div>, la largeur de la fenêtre d'affichage ("viewport", celle où notre document est affiché) et sa hauteur, avant de les stocker dans des variables — ces deux dernières valeurs sont faciles à obtenir via les propriétés Window.innerWidth et Window.innerHeight. Ajoutez les lignes qui suivent à l'intérieur de l'élément <script> : -
    const div = document.querySelector('div');
    -      let winWidth = window.innerWidth;
    -      let winHeight = window.innerHeight;
    -
  2. -
  3. Ensuite, nous allons modifier dynamiquement la largeur et la hauteur de notre <div> pour qu'elles soient égales à celles de la fenêtre d'affichage. Ajoutez les lignes suivantes à la suite des précédentes : -
    div.style.width = winWidth + 'px';
    -    div.style.height = winHeight + 'px';
    -
  4. -
  5. Sauvegardez vos modifications et rafraîchissez votre page : vous devriez désormais constater que la <div> est aussi grande que la fenêtre, quelle que soit la taille de la fenêtre. Si maintenant vous essayez d'agrandir votre fenêtre, vous pouvez constater que la div ne change pas de taille — nous ne définissons la taille qu'une seule fois.
  6. -
  7. Nous pouvons utiliser un gestionnaire d'événement pour que la <div> soit redimensionnée à chaque fois que la fenêtre l'est. L'objet Window dispose d'un événement dédié appelé resize, qui est déclenché à chaque fois que la fenêtre est redimensionnée — nous pouvons utiliser le gestionnaire d'événement {{domxref("GlobalEventHandlers/onresize", "Window.onresize")}} pour ré-exécuter notre code de dimensionnement à chaque fois. Ajoutez ce qui suit au bas de votre code: -
    window.onresize = function() {
    -    winWidth = window.innerWidth;
    -    winHeight = window.innerHeight;
    -    div.style.width = winWidth + 'px';
    -    div.style.height = winHeight + 'px';
    -  }
    -
  8. -
- -
-

Note : En cas de blocage, jetez un œil à notre exemple de redimensionnement de la fenêtre terminé sur GitHub (voir en direct aussi).

-
- -

Apprentissage actif : Une liste de courses dynamique

- -

Pour clore cet article, nous aimerions vous proposer un petit challenge : nous voulons créer une liste de courses simple qui nous permette d'ajouter des articles à la liste de façon dynamique, le tout grâce à un champ de formulaire et un bouton. Quand vous ajoutez une valeur au champ et appuyez sur le bouton :

- - - -

La démo terminée doit ressembler à ça:

- -

- -

Pour compléter l'exercice, suivez les étapes ci-dessous, et assurez-vous que votre exemple se comporte comme décrit ci-dessus.

- -
    -
  1. Tout d'abord, téléchargez une copie du fichier shopping-list.html. Vous verrez qu'il contient : un peu de CSS, une liste avec un titre, un champ, un bouton, une liste vide et un élément <script>. Vous apporterez toutes vos modifications à l'intérieur du script.
  2. -
  3. Créez trois variables, contenant des références aux éléments de liste <ul>, de champ <input> et de bouton <button>.
  4. -
  5. Créez une fonction qui sera déclenchée lorsqu'on clique sur le bouton.
  6. -
  7. À l'intérieur du corps de la fonction, commencez par stocker la valeur actuelle (propriété value) du champ dans une variable.
  8. -
  9. Ensuite, videz le champ en définissant sa valeur comme une chaîne vide — ''.
  10. -
  11. Créez trois nouveaux éléments : un élément de liste <li>, un <span> et un bouton <button>, et stockez-les chacun dans des variables.
  12. -
  13. Attachez le <span> et le <button> comme enfants de <li>.
  14. -
  15. Définissez le contenu texte du <span> comme égal à la valeur du champ que vous avez récupéré précédemment, et le contenu du bouton à "Supprimer".
  16. -
  17. Attachez l'article <li> comme enfant de la liste.
  18. -
  19. Ajoutez un gestionnaire d'événement au bouton "Supprimer", de façon à ce que lorsqu'on le clique le <li> dans lequel il se situe soit supprimé.
  20. -
  21. Enfin, utilisez la méthode HTMLElement.focus pour donner le focus au champ, qu'il soit prêt à recevoir la valeur du prochain article de la liste de courses.
  22. -
- -
-

Note : Si vous bloquez vraiment, jetez un œil à notre liste de courses terminée (voir en direct.)

-
- -

Résumé

- -

Nous avons fini notre étude de la manipulation de document et du DOM. À ce stade, vous devriez comprendre quels sont les composants importants d'un navigateur web en matière de contrôle de documents et certains aspects de l'expérience utilisateur sur le Web. Plus important encore, vous devriez comprendre ce qu'est le Document Object Model, et comment l'utiliser pour créer des fonctionnalités utiles.

- -

Voir aussi

- -

Il y a bien d'autres fonctionnalités que vous pouvez utiliser pour manipuler vos documents. Jetez un coup d'œil à quelques-unes de nos notices pour en découvrir davantage :

- - - -

(Voir notre Référence Web API pour une liste complète des APIs web documentées sur MDN!)

- -
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}
- - -

Dans ce module

- - \ No newline at end of file diff --git a/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.md b/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.md new file mode 100644 index 0000000000..8025213ac4 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/manipulating_documents/index.md @@ -0,0 +1,332 @@ +--- +title: Manipuler des documents +slug: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents +tags: + - API + - Apprendre + - Article + - Codage + - DOM + - Document Object Model + - Débutant + - JavaScript + - Navigator + - WebAPI + - Window +translation_of: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Manipulating_documents +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

Quand on écrit des pages web et des applications, une des choses les plus courantes que l'on veut faire est de manipuler la structure du document d'une manière ou d'une autre. On le fait généralement en utilisant le Document Object Model (DOM), un ensemble d'APIs qui permettent de contrôler le HTML et le style — et qui utilisent massivement l'objet Document. Dans cet article, nous allons voir comment utiliser le DOM en détail, ainsi que quelques APIs intéressantes qui peuvent modifier votre environnement.

+ + + + + + + + + + + + +
Prérequis :Connaissances de base en informatique, notions de base d'HTML, CSS, et JavaScript — dont les objets JavaScript.
Objectif :Se familiariser avec les APIs DOM, et autres APIs souvent associées au DOM et à la manipulation de document
+ +

Les principaux composants du navigateur

+ +

Les navigateurs web sont des logiciels très complexes avec beaucoup de composants, dont beaucoup ne peuvent pas être contrôlés ou manipulés en utilisant JavaScript. Vous pourriez penser que de telles limitations sont une mauvaise chose, mais les navigateurs sont verrouillés pour de bonnes raisons, la plupart du temps pour des raisons de sécurité. Imaginez qu'un site web puisse accéder à vos mots de passe stockés ou à d'autres informations sensibles, ou se connecter à des sites web comme si c'était vous?

+ +

Malgré les limitations, les APIs Web nous donnent accès à beaucoup de fonctionnalités, lesquelles nous permettent de faire plein de choses géniales avec les pages web. Il existe quelques éléments évidents que vous utilisez régulièrement dans votre code — jetez un coup d'œil au diagramme suivant, il représente les principaux composants du navigateur directement impliqués dans l'affichage des pages web:

+ +

+ + + +

Dans cet article, nous allons principalement nous concentrer sur la manipulation du document, mais nous verrons également quelques autres éléments utiles.

+ +

Le modèle objet du document (Document Object Model)

+ +

Le document chargé dans chaque onglet de votre navigateur, et donc son contenu, est accessible via un modèle objet du document — Document Objet Model en anglais, ou DOM. Il s'agit d'une structure arborescente créée par le navigateur et qui permet aux langages de programmation d'accéder facilement à la structure HTML — par exemple, le navigateur lui-même l'utilise pour appliquer différents styles aux éléments correspondants sur la page, tandis qu'un développeur comme vous et moi peut l'utiliser pour manipuler le DOM avec du JavaScript après que la page ait été chargée.

+ +

Nous avons créé une simple page d'exemple, dom-example.html (voir en direct). Essayez de l'ouvrir dans votre navigateur — c'est une page très simple qui contient un élément <section>, à l'intérieur duquel se trouve une image et un paragraphe avec un lien. Le code source HTML ressemble à ça:

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Simple DOM example</title>
+  </head>
+  <body>
+      <section>
+        <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
+        <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
+      </section>
+  </body>
+</html>
+ +

Le DOM, quant à lui, ressemble à ça :

+ +

+ +
+

Note : Ce diagramme du DOM a été créé en utilisant le Live DOM viewer de Ian Hickson.

+
+ +

Vous pouvez voir ici que chaque élément et morceau de texte dans le document possède sa propre entrée dans l'arbre — chacune étant appelée nœud (node). Vous rencontrerez également plusieurs termes pour décrire les différents type de nœuds ou leur position dans l'arbre les uns par rapport aux autres :

+ + + +

Il est utile de vous familiariser avec ces termes avant de travailler avec le DOM, puisqu'un certain nombre de documentations les utilisent. Vous les avez peut-être déjà rencontrés si vous avez étudié le CSS (ex. sélecteur descendant, sélecteur enfant).

+ +

Apprentissage actif : Manipulations basiques du DOM

+ +

Pour commencer l'apprentissage de la manipulation du DOM, commençons par un exemple concret.

+ +
    +
  1. Faites une copie locale de la page dom-example.html et de l'image qui l'accompagne.
  2. +
  3. Ajoutez un élément <script></script> juste avant la balise fermante </body>.
  4. +
  5. Pour manipuler un élément dans le DOM, vous allez d'abord sélectionner cet élément et stocker une référence à cet élément dans une variable. À l'intérieur de votre élément <script>, ajoutez la ligne suivante: +
    const link = document.querySelector('a');
    +
  6. +
  7. Maintenant que nous avons la référence à l'élément enregistrée dans une variable, nous pouvons commencer à le manipuler en utilisant les propriétés et les méthodes qui lui sont associées (celles-ci sont définies sur les interfaces telles que HTMLAnchorElement dans le cas d'un élément <a>, et sur les interfaces plus génériques HTMLElement, et Node — qui représente tous les nœuds d'un DOM). Tout d'abord, changeons le texte du lien en mettant à jour la valeur de la propriété Node.textContent. Ajoutez la ligne suivante à la suite de la précédente : +
    link.textContent = 'Mozilla Developer Network';
    +
  8. +
  9. Nous devons également modifier l'URL ciblée par le lien, pour qu'il ne renvoie pas au mauvais endroit quand on clique dessus. Ajoutez la ligne suivante, en bas de votre JavaScript : +
    link.href = 'https://developer.mozilla.org';
    +
  10. +
+ +
+

Notez que, comme souvent en JavaScript, il y a plusieurs façons de sélectionner et d'enregistrer une référence à un élément dans une variable. Document.querySelector() est l'approche moderne recommandée — elle est pratique puisqu'elle permet de sélectionner des éléments en utilisant les sélecteurs CSS. L'appel à querySelector() que nous avons utilisé plus tôt récupère le premier élément <a> qui apparaît dans le document. Si vous souhaitez au contraire récupérer plusieurs éléments, vous pouvez utiliser Document.querySelectorAll(), qui récupère tous les éléments du document correspondant au sélecteur, et retourne des références vers ces éléments dans un objet similaire à un tableau appelé un NodeList.

+ +

Il existe des méthodes plus anciennes pour récupérer des références aux éléments, telles que :

+ + + +

Ces deux dernières méthodes fonctionnent mieux dans les navigateurs plus anciens que des méthodes plus modernes comme querySelector(), mais elles sont beaucoup moins pratiques. Regardez autour de vous et essayez d'en trouver d'autres!

+
+ +

Créer et placer de nouveaux nœuds

+ +

Ce qui précède vous a donné un petit avant-goût de ce que vous pouvez faire, mais allons plus loin et regardons comment créer de nouveaux éléments.

+ +
    +
  1. Pour revenir à notre exemple, commençons par récupérer une référence à notre élément <section> — ajoutez le code suivant au bas de votre script existant (idem avec les lignes qui suivront) : +
    var sect = document.querySelector('section');
    +
  2. +
  3. Nous allons maintenant créer un nouveau paragraphe grâce à Document.createElement(), et lui donner du contenu texte de la même manière que précédemment : +
    var para = document.createElement('p');
    +para.textContent = 'We hope you enjoyed the ride.';
    +
  4. +
  5. Nous pouvons à présent ajouter ce paragraphe au bas de la section en utilisant Node.appendChild(): +
    sect.appendChild(para);
    +
  6. +
  7. Enfin, ajoutons un nœud texte au premier paragraphe, pour finir la phrase joliment. Créons d'abord un nœud texte avec Document.createTextNode() : +
    var text = document.createTextNode(' — the premier source for web development knowledge.');
    +
  8. +
  9. Puis, après avoir récupéré une référence au premier paragraphe, ajoutons-y le nœud texte: +
    var linkPara = document.querySelector('p');
    +linkPara.appendChild(text);
    +
  10. +
+ +

C'est l'essentiel de ce dont vous aurez besoin pour ajouter des nœuds au DOM — vous utiliserez beaucoup ces méthodes lorsque vous construirez des interfaces dynamiques (nous en verrons quelques exemples plus tard).

+ +

Déplacer et supprimer des éléments

+ +

Il peut arriver qu'on veuille déplacer des nœuds, ou même les supprimer du DOM. C'est tout à fait possible, et voyons comment.

+ + +

Par exemple, si l'on veut déplacer le premier paragraphe de notre exemple au bas de la section, on pourrait utiliser :

+ +
sect.appendChild(linkPara);
+ +

Cette commande va déplacer le paragraphe tout au bas de la section. On pourrait penser qu'elle va en fait ajouter une copie, mais ce n'est pas le cas : linkPara ne fait référence qu'à un seul et unique élément. Pour copier le paragraphe, il faudrait utiliser Node.cloneNode() à la place.

+ +

Supprimer des éléments est également plutôt simple, dès lors qu'on a une référence de l'élément à supprimer et de son parent. Dans notre cas, on peut simplement utiliser Node.removeChild(), comme ceci :

+ +
sect.removeChild(linkPara);
+ +

Si vous souhaitez un élément uniquement à partir d'une référence à cet élément, comme c'est souvent le cas, vous pouvez utiliser ChildNode.remove() :

+ +
linkPara.remove();
+ +

Cette méthode ne fonctionne cependant pas dans les navigateurs plus anciens. Ils ne possèdent en effet pas de méthodes pour dire à un nœud de se supprimer, et il faut donc procéder comme suit :

+ +
linkPara.parentNode.removeChild(linkPara);
+ +

À votre tour : essayez les lignes ci-dessus en les ajoutant à votre code.

+ +

Manipuler le style

+ +

Il est possible de manipuler des styles CSS grâce à du JavaScript de plusieurs manières.

+ +

Pour commencer, vous pouvez obtenir une liste de toutes les feuilles de style associées à un document en utilisant Document.stylesheets, qui retourne un objet, ressemblant à un tableau composé d'objets CSSStyleSheet. Vous pouvez alors ajouter/supprimer des styles comme vous le souhaitez. Cependant, nous n'allons pas nous étendre sur ces fonctionnalités, car elles sont archaïques et il est difficile de manipuler le style avec. Il y a des techniques beaucoup plus simples.

+ +

La première d'entre elles consiste à ajouter des styles en ligne (inline styles), directement sur les éléments que vous voulez styliser de façon dynamique. Pour ce faire, on utilise la propriété HTMLElement.style, qui contient les informations de style en ligne de chaque élément du document. Vous pouvez définir des propriétés de cet objet de façon à pouvoir mettre à jour directement le style des éléments.

+ +
    +
  1. À titre d'exemple, essayez d'ajouter les lignes suivantes à notre exemple : +
    para.style.color = 'white';
    +para.style.backgroundColor = 'black';
    +para.style.padding = '10px';
    +para.style.width = '250px';
    +para.style.textAlign = 'center';
    +
  2. +
  3. Rafraichissez la page, et vous verrez que les styles ont été appliqués au paragraphe. Si vous regardez ce paragraphe dans l'Inspecteur du navigateur, vous verrez que ces lignes sont en effet ajoutées comme du style en ligne au document: +
    <p style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;">We hope you enjoyed the ride.</p>
    +
  4. +
+ +
+

Note : Vous remarquerez que les propriétés JavaScript qui représentent les propriétés CSS sont écrites en lower camel case tandis que les versions CSS sont reliées par des tirets (par exemple backgroundColor au lieu de background-color). Prenez garde à ne pas les mélanger, sans quoi ça ne fonctionnera pas.

+
+ +

Il y a un autre moyen de manipuler dynamiquement des styles sur votre document, que nous allons étudier maintenant.

+ +
    +
  1. Supprimez les cinq lignes précédentes que nous avons ajoutées à notre code JavaScript.
  2. +
  3. Ajoutez ce qui suit au sein de la balise <head> de votre HTML: +
    <style>
    +.highlight {
    +  color: white;
    +  background-color: black;
    +  padding: 10px;
    +  width: 250px;
    +  text-align: center;
    +}
    +</style>
    +
  4. +
  5. Nous allons maintenant utiliser une méthode très utile pour la manipulation HTML de manière générale : Element.setAttribute(). Cette fonction prend deux paramètres : le nom de l'attribut que vous voulez définir sur l'élément, et la valeur que vous voulez lui attribuer. Ici nous allons ajouter une classe highlight à notre élément : +
    para.setAttribute('class', 'highlight');
    +
  6. +
  7. Rafraîchissez votre page, et vous constaterez qu'il n'y a aucun changement par rapport au dernier exemple. Le CSS est toujours appliqué au paragraphe, mais la seule différence c'est qu'on a utilisé une classe pour le faire et non des styles en ligne.
  8. +
+ +

A vous de choisir la méthode que vous souhaitez utiliser : chacune a ses avantages et ses inconvénients. Les styles en ligne demandent moins de préparation et sont utiles pour un usage simple, tandis que l'usage des classes est une méthode plus pure (on ne mélange pas le CSS et le JavaScript, on évite donc les styles en ligne, car c'est considéré comme une mauvaise pratique). Au fur et à mesure que vous construirez des applications plus volumineuses et complexes, vous allez probablement utiliser la deuxième méthode plus souvent, mais c'est à vous de décider.

+ +

À ce stade, nous n'avons pas vraiment fait quoi que soit d'utile! Il n'y a pas d'intérêt à générer du contenu statique avec du JavaScript — autant l'écrire directement en HTML sans passer par du JavaScript. Le JavaScript est plus complexe que du HTML, et comporte son propre lot de problèmes (comme le fait qu'il ne puisse pas être lu par les moteurs de recherche).

+ +

Dans les deux prochaines sections, nous verrons des exemples d'utilisation plus pratiques des APIs du DOM.

+ +
+

Note : Vous pouvez trouver la version finale de dom-example.html sur GitHub (le voir en direct aussi).

+
+ +

Apprentissage actif : Récupérer des informations utiles depuis l'objet Window

+ +

Jusqu'à présent nous avons seulement utilisé les fonctionnalités de Node et Document (le DOM) pour manipuler les documents, mais vous pouvez obtenir des données d'autres sources pour les intégrer à votre interface utilisateur (UI). Il faut simplement s'assurer que les données sont au bon format ; c'est plus simple avec JavaScript qu'avec d'autres langages, puisqu'on utilise un typage faible — les nombres par exemple sont automatiquement convertis en texte lorsqu'on les affiche à l'écran.

+ +

Dans cet exemple, nous allons résoudre un problème très courant — s'assurer que votre application est de la même taille que la fenêtre, quelle que soit la taille de la fenêtre. C'est souvent utile pour des jeux par exemple, où l'on veut utiliser autant d'espace d'écran que possible pour jouer.

+ +

Pour commencer, faites une copie en local des fichiers de démonstration window-resize-example.html et bgtile.png. Ouvrez-les : vous y trouverez un élément <div> qui couvre une petite partie de l'écran avec un motif en mosaïque. Nous utiliserons cet élément pour représenter la surface de notre interface utilisateur.

+ +
    +
  1. Tout d'abord, nous allons récupérer : une référence au <div>, la largeur de la fenêtre d'affichage ("viewport", celle où notre document est affiché) et sa hauteur, avant de les stocker dans des variables — ces deux dernières valeurs sont faciles à obtenir via les propriétés Window.innerWidth et Window.innerHeight. Ajoutez les lignes qui suivent à l'intérieur de l'élément <script> : +
    const div = document.querySelector('div');
    +      let winWidth = window.innerWidth;
    +      let winHeight = window.innerHeight;
    +
  2. +
  3. Ensuite, nous allons modifier dynamiquement la largeur et la hauteur de notre <div> pour qu'elles soient égales à celles de la fenêtre d'affichage. Ajoutez les lignes suivantes à la suite des précédentes : +
    div.style.width = winWidth + 'px';
    +    div.style.height = winHeight + 'px';
    +
  4. +
  5. Sauvegardez vos modifications et rafraîchissez votre page : vous devriez désormais constater que la <div> est aussi grande que la fenêtre, quelle que soit la taille de la fenêtre. Si maintenant vous essayez d'agrandir votre fenêtre, vous pouvez constater que la div ne change pas de taille — nous ne définissons la taille qu'une seule fois.
  6. +
  7. Nous pouvons utiliser un gestionnaire d'événement pour que la <div> soit redimensionnée à chaque fois que la fenêtre l'est. L'objet Window dispose d'un événement dédié appelé resize, qui est déclenché à chaque fois que la fenêtre est redimensionnée — nous pouvons utiliser le gestionnaire d'événement {{domxref("GlobalEventHandlers/onresize", "Window.onresize")}} pour ré-exécuter notre code de dimensionnement à chaque fois. Ajoutez ce qui suit au bas de votre code: +
    window.onresize = function() {
    +    winWidth = window.innerWidth;
    +    winHeight = window.innerHeight;
    +    div.style.width = winWidth + 'px';
    +    div.style.height = winHeight + 'px';
    +  }
    +
  8. +
+ +
+

Note : En cas de blocage, jetez un œil à notre exemple de redimensionnement de la fenêtre terminé sur GitHub (voir en direct aussi).

+
+ +

Apprentissage actif : Une liste de courses dynamique

+ +

Pour clore cet article, nous aimerions vous proposer un petit challenge : nous voulons créer une liste de courses simple qui nous permette d'ajouter des articles à la liste de façon dynamique, le tout grâce à un champ de formulaire et un bouton. Quand vous ajoutez une valeur au champ et appuyez sur le bouton :

+ + + +

La démo terminée doit ressembler à ça:

+ +

+ +

Pour compléter l'exercice, suivez les étapes ci-dessous, et assurez-vous que votre exemple se comporte comme décrit ci-dessus.

+ +
    +
  1. Tout d'abord, téléchargez une copie du fichier shopping-list.html. Vous verrez qu'il contient : un peu de CSS, une liste avec un titre, un champ, un bouton, une liste vide et un élément <script>. Vous apporterez toutes vos modifications à l'intérieur du script.
  2. +
  3. Créez trois variables, contenant des références aux éléments de liste <ul>, de champ <input> et de bouton <button>.
  4. +
  5. Créez une fonction qui sera déclenchée lorsqu'on clique sur le bouton.
  6. +
  7. À l'intérieur du corps de la fonction, commencez par stocker la valeur actuelle (propriété value) du champ dans une variable.
  8. +
  9. Ensuite, videz le champ en définissant sa valeur comme une chaîne vide — ''.
  10. +
  11. Créez trois nouveaux éléments : un élément de liste <li>, un <span> et un bouton <button>, et stockez-les chacun dans des variables.
  12. +
  13. Attachez le <span> et le <button> comme enfants de <li>.
  14. +
  15. Définissez le contenu texte du <span> comme égal à la valeur du champ que vous avez récupéré précédemment, et le contenu du bouton à "Supprimer".
  16. +
  17. Attachez l'article <li> comme enfant de la liste.
  18. +
  19. Ajoutez un gestionnaire d'événement au bouton "Supprimer", de façon à ce que lorsqu'on le clique le <li> dans lequel il se situe soit supprimé.
  20. +
  21. Enfin, utilisez la méthode HTMLElement.focus pour donner le focus au champ, qu'il soit prêt à recevoir la valeur du prochain article de la liste de courses.
  22. +
+ +
+

Note : Si vous bloquez vraiment, jetez un œil à notre liste de courses terminée (voir en direct.)

+
+ +

Résumé

+ +

Nous avons fini notre étude de la manipulation de document et du DOM. À ce stade, vous devriez comprendre quels sont les composants importants d'un navigateur web en matière de contrôle de documents et certains aspects de l'expérience utilisateur sur le Web. Plus important encore, vous devriez comprendre ce qu'est le Document Object Model, et comment l'utiliser pour créer des fonctionnalités utiles.

+ +

Voir aussi

+ +

Il y a bien d'autres fonctionnalités que vous pouvez utiliser pour manipuler vos documents. Jetez un coup d'œil à quelques-unes de nos notices pour en découvrir davantage :

+ + + +

(Voir notre Référence Web API pour une liste complète des APIs web documentées sur MDN!)

+ +
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}
+ + +

Dans ce module

+ + \ No newline at end of file diff --git a/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.html b/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.html deleted file mode 100644 index 15dfb1c063..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.html +++ /dev/null @@ -1,441 +0,0 @@ ---- -title: API tierces -slug: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs -tags: - - API - - Apprendre - - Débutant -translation_of: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Third_party_APIs ---- -
{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}
- -

Jusqu'à présent, nous avons uniquement abordé des API qui sont fournies par le navigateur. Il en existe d'autres : de nombreux sites et services, tels que Google Maps, Twitter, Facebook, PayPal, etc. fournissent des API permettant aux développeurs d'exploiter leurs données (ex. afficher un flux Twitter sur un blog) ou leurs services (utiliser l'outil de connexion Facebook pour que vos utilisateurs se connectent sur votre application). Dans cet article, nous verrons les différences entre les API du navigateur et celles fournies par des services tiers (en anglais, on parle de « third-party API ») et nous illustrerons certains cas d'usage pour ces API tierces.

- - - - - - - - - - - - -
Prérequis :Les bases de JavaScript (voir premiers pas, blocs de construction, les objets JavaScript), les bases des API côté client.
Objectifs :Comprendre le fonctionnement des API tierces et comment les utiliser pour ajouter des fonctionnalités à ses sites / applications.
- -

Qu'est-ce qu'une API tierce ?

- -

Les API tierces sont des API qui sont fournis par des tiers, généralement des entreprises comme Facebook, Twitter ou Google, qui permettent d'accéder à leurs données et/ou leurs fonctionnalités grâce à JavaScript afin de les utiliser sur son site. Utiliser une API de cartographie afin d'afficher une carte sur une page est un exemple.

- -

Regardons cet exemple qui utilise l'API MapQuest et voyons avec lui les différences entre les API tierces et celles du navigateur.

- -
-

Note : Vous pouvez récupérer l'ensemble des exemples de code en une seule fois. Dans ce cas, il vous suffit de rechercher dans votre dépôt les fichiers utilisés pour chaque section.

-
- -

Elles sont situées sur des serveurs tiers

- -

Les API fournies par le navigateur sont construites dans le navigateur et on peut y accéder immédiatement avec du code JavaScript. Ainsi, l'API Web Audio que nous avons vu dans cet article introductif s'utilise via l'objet {{domxref("AudioContext")}} fourni nativement :

- -
const audioCtx = new AudioContext();
-  ...
-const audioElement = document.querySelector('audio');
-  ...
-const audioSource = audioCtx.createMediaElementSource(audioElement);
-// etc.
- -

En revanche, les API tierces sont situées sur des serveurs tiers. Pour y accéder avec JavaScript, il faut d'abord se connecter aux fonctionnalités de l'API afin de les rendre disponibles sur la page. Pour cela, on pourra utiliser une bibliothèque JavaScript disponible sur le serveur via un élément {{htmlelement("script")}}. Pour notre exemple avec MapQuest, voici ce que ça donne :

- -
<script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"></script>
-<link type="text/css" rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"/>
- -

On peut alors utiliser les objets fournis par cette bibliothèque. Voici un fragment de code qui illustre cette utilisation :

- -
var L;
-
-var map = L.mapquest.map('map', {
-  center: [53.480759, -2.242631],
-  layers: L.mapquest.tileLayer('map'),
-  zoom: 12
-});
- -

Ici on crée une variable dans laquelle enregistrer les informations de la carte puis on crée une nouvelle carte à l'aide de la méthode mapquest.map() qui prend comme argument :

- - - -

C'est tout ce dont l'API MapQuest a besoin pour dessiner une carte. C'est le serveur auquel on se connecte qui gère les aspects plus compliqués (comme afficher les bonnes tuiles pour la zone géographique, etc.).

- -
-

Note : Certaines API fonctionnent différemment pour l'accès aux fonctionnalités et passent par une requête HTTP sur une URL spécifique pour récupérer les données. Ces API sont appelées API REST (ou RESTful APIs en anglais) et nous les abordons plus bas dans l'article.

-
- -

Des clés d'API sont nécessaires pour les utiliser

- -

Dans les navigateurs, comme nous l'avons vu dans le premier article, les sécurités relatives aux API sont gérées via des permissions afin d'avertir l'utilisateur et d'éviter les utilisations malveillantes de la part des sites.

- -

Pour les API tierces, le système est légèrement différent. Généralement, ce sont des clés qui sont utilisées afin de fournir aux développeurs l'accès aux fonctionnalités de l'API. Dans ce système, la clé sert plutôt à protéger des abus de la part des développeurs envers le site tiers.

- -

Dans l'exemple MapQuest, vous pourrez trouver une ligne semblable à celle-ci :

- -
L.mapquest.key = 'VOTRE-CLE-D-API-ICI';
- -

Cette ligne indique une clé d'API utilisée par notre application. Le développeur de l'application doit obtenir une clé et l'inclure dans le code de l'application afin de pouvoir accéder aux fonctionnalités de l'API. Pour cet exemple, il s'agit d'une valeur imaginaire.

- -
-

Note : Lorsque vous construisez votre propre application, utilisez une « vraie » valeur de clé plutôt que la valeur de substitution fournie en exemple.

-
- -

Certaines API peuvent nécessiter de fournir la clé d'une façon différente mais le fonctionnement général reste similaire.

- -

L'existence d'une telle clé d'API permet au fournisseur tiers de contrôler les accès et actions des différents développeurs/utilisateurs. Ainsi, lorsqu'un développeur demande une clé, il devient alors connu du fournisseur de l'API et ce dernier peut agir de son côté si l'API est détournée ou utilisée de façon incorrecte (par exemple pour pister des personnes ou parce que le site du développeur sollicite l'API avec de trop nombreuses requêtes). Dès qu'un usage incorrect est détecté du côté du fournisseur, il suffit alors de révoquer l'accès et de couper ou de limiter les accès pour cette clé.

- -

Étendre l'exemple « MapQuest »

- -

Ajoutons quelques fonctionnalités à cet exemple MapQuest afin d'illustrer le fonctionnement d'autres aspects de l'API.

- -
    -
  1. -

    Pour commencer cette section, copiez le fichier initial dans un nouveau répertoire. Si vous avez déjà cloné le dépôt des exemples, vous disposez déjà d'une copie située sous le répertoire javascript/apis/third-party-apis/mapquest.

    -
  2. -
  3. -

    Ensuite, rendez-vous sur le site MapQuest pour les développeurs, créez un compte puis créez une clé de développement (au moment où nous écrivons ces lignes, sur le site, le nom utilisé est "consumer key" et la procédure de création demande aussi la saisie d'une URL "callback URL" qui est optionnelle (vous pouvez la laisser vide).

    -
  4. -
  5. Ouvrez un éditeur pour éditer le fichier initial et remplacez la valeur pour la clé d'API avec la vôtre.
  6. -
- -

Modifier le type de la carte

- -

L'API MapQuest permet d'afficher différents types de carte. Localisez la ligne suivante dans votre fichier :

- -
layers: L.mapquest.tileLayer('map')
- -

Ici, vous pouvez changer 'map' en 'hybrid' afin d'afficher une carte avec un style hybride. Vous pouvez essayer d'autres valeurs comme celles indiquées sur la page de référence de la méthode tileLayer().

- -

Ajouter différents contrôles

- -

Sur la carte, on peut utiliser différents contrôles. Par défaut, seul un contrôle pour le zoom est affiché. Il est possible d'étendre les contrôles disponibles en utilisant la méthodemap.addControl(). Ajoutez la ligne suivante à l'intérieur du gestionnaire window.onload :

- -
map.addControl(L.mapquest.control());
- -

La méthode mapquest.control() crée un ensemble complet de contrôle et est placée, par défaut, dans le coin supérieur droit de la carte. Cette position peut être ajustée grâce à un paramètre d'options contenant une propriété position dont la valeur est un mot-clé décrivant la position souhaitée. Vous pouvez modifier la ligne de la façon suivante par exemple :

- -
  map.addControl(L.mapquest.control({ position: 'bottomright' }));
- -

D'autres types de contrôle sont disponibles comme mapquest.searchControl() et mapquest.satelliteControl(). Certains sont avancés et permettent de nombreuses choses. N'hésitez pas à en essayer différents pour voir ce qui vous semble le mieux pour votre projet.

- -

Ajouter un marqueur personnalisé

- -

Pour ajouter une icône sur un point de la carte, on pourra utiliser la méthode L.marker()  (documentée dans la documentation de Leaflet.js). Dans window.onload, vous pouvez ajouter le fragment de code suivante window.onload :

- -
L.marker([53.480759, -2.242631], {
-  icon: L.mapquest.icons.marker({
-    primaryColor: '#22407F',
-    secondaryColor: '#3B5998',
-    shadow: true,
-    size: 'md',
-    symbol: 'A'
-  })
-})
-.bindPopup('Ici se trouve Manchester !')
-.addTo(map);
- -

Comme on peut le voir ici, la méthode peut prendre deux paramètres :

- - - -

L'icône est définie via un appel à la méthode mapquest.icons.marker() à laquelle on fournit des informations telles que la couleur et la taille du marqueur.

- -

Après l'appel à la première méthode, on enchaîne avec un appel avec .bindPopup('Ici se trouve Manchester !'), qui fournit le contenu à afficher lorsqu'on cliquera sur le marqueur.

- -

Enfin, on chaîne un appel .addTo(map) pour ajouter le marqueur sur la carte.

- -

Essayez les différentes options que vous trouverez dans la documentation et voyez quel résultat vous pouvez obtenir ! MapQuest fournit certaines fonctionnalités relativement avancées (itinéraire, recherche, etc.).

- -
-

Note : Si vous ne parvenez pas à faire fonctionner l'exemple, vous pouvez consulter la version finalisée de notre exemple : expanded-example.html.

-
- -

Quid de Google Maps ?

- -

Google Maps est sans doute l'API de cartographie la plus populaire. Pourquoi ne l'avons-nous pas incluse ici ? Nous avons choisi MapQuest pour plusieurs raisons :

- - - -

Une approche pour utiliser les API tierces

- -

Une API REST du NYTimes

- -

Prenons un autre exemple d'API, l'API du New York Times. Cette API permet de récupérer les informations provenant du New York Times et de les afficher sur votre site. Cette API est ce qu'on appelle une API REST car elle permet de récupérer des données via des requêtes HTTP sur des URL spécifiques dans lesquelles on peut fournir des données comme des termes de recherches (souvent encodés comme paramètres dans l'URL). Ce type d'API est relativement fréquent.

- -

Construisons un second exemple pour comprendre comment utiliser l'API du NYTimes. Au fur et à mesure nous décrirons également une approche plus générale pour consommer d'autres API tierces.

- -

Trouver la documentation

- -

Lorsqu'on veut utiliser une API tierce, il est toujours utile de trouver la documentation correspondante pour connaître les fonctionnalités offertes, comment les utiliser, etc. La documentation de l'API du New York Times API se situe ici : https://developer.nytimes.com/.

- -

Obtenir une clé de développement

- -

La plupart des API reposent sur l'obtention et l'utilisation d'une clé de développement (tant pour des raisons de sécurité que de responsabilité). Pour obtenir une clé de développement pour l'API du NYTimes, vous pouvez suivre les instructions de https://developer.nytimes.com/get-started.

- -
    -
  1. -

    Demandez une clé pour l'API Article Search — créez une nouvelle application et sélectionnez cette API, fournissez un nom et une description pour votre application, activez le bouton sous "Article Search API" puis cliquez sur "Create").

    -
  2. -
  3. -

    Vous pouvez alors récupérer la clé d'API à partir de la page suivante.

    -
  4. -
  5. -

    Pour construire le socle de notre exemple, copiez nytimes_start.html et nytimes.css dans un nouveau répertoire sur votre ordinateur. Si vous avez déjà cloné le dépôt des exemples, vous disposez déjà d'un exemplaire de ces fichiers et vous pourrez les trouver sous le répertoire javascript/apis/third-party-apis/nytimes. L'élément <script> contient un certain nombre de variables nécessaires à l'initialisation de l'exemple. Nous allons ensuite remplir les fonctionnalités nécessaires.

    -
  6. -
- -

Au final, on souhaite que l'application permette de saisir un terme de recherche, des dates optionnelles pour le début et la fin de la période à rechercher. Nous utiliserons alors ces paramètres afin de d'envoyer des requêtes sur l'API Article Search puis nous afficherons les résultats obtenus.

- -

- -

Connecter l'API à son application

- -

Tout d'abord, vous devrez créer une connexion entre l'API et votre application. Pour cette API, vous devez fournir la clé d'API comme paramètre GET à chaque requête.

- -
    -
  1. -

    Localisez la ligne qui suit et remplacez la valeur avec la clé de développement que vous avez obtenu plus tôt :

    - -
    var key = ' ... ';
    -
  2. -
  3. -

    Ajoutez la ligne suivante sous le commentaire // Event listeners to control the functionality. Cette ligne permet d'exécuter la fonction submitSearch() lorsque le formulaire est envoyé (quand on presse le bouton).

    - -
    searchForm.addEventListener('submit', submitSearch);
    -
  4. -
  5. -

    Sous cette nouvelle ligne, ajoutons les fonctions submitSearch() et fetchResults() :

    - -
    function submitSearch(e) {
    -  pageNumber = 0;
    -  fetchResults(e);
    -}
    -
    -function fetchResults(e) {
    -  // On utilise preventDefault() afin d'éviter
    -  // l'envoie vers notre serveur et le rafraîchissement
    -  // de la page
    -  e.preventDefault();
    -
    -  // On compose l'URL
    -  url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
    -
    -  if(startDate.value !== '') {
    -    url += '&begin_date=' + startDate.value;
    -  };
    -
    -  if(endDate.value !== '') {
    -    url += '&end_date=' + endDate.value;
    -  };
    -
    -}
    -
  6. -
- -

submitSearch() commence par réinitialiser le nombre de page à 0 puis appelle fetchResults(). Cette fonction commence par appeler preventDefault() sur l'évènement afin d'empêcher l'envoi du formulaire vers notre serveur (ce qui casserait l'exemple). Ensuite, on assemble la chaîne de caractères qui formera l'URL sur laquelle on fera la requête. Dans cette URL, on commence par mettre les fragments « obligatoires » (en tout cas pour cette démonstration) :

- - - -

Après, on utilise un ensemble d'instructions if() pour vérifier si des valeurs ont été fournies pour les champs startDate et endDate dans le formulaire. Si c'est le cas, on utilise ces valeurs pour renseigner les paramètres d'URL begin_date et/ou end_date.

- -

Si on prend un exemple d'URL complète ainsi construite :

- -
https://api.nytimes.com/svc/search/v2/articlesearch.json?api-key=YOUR-API-KEY-HERE&page=0&q=cats
-&fq=document_type:("article")&begin_date=20170301&end_date=20170312 
- -
-

Note : Pour en savoir plus sur les différents paramètres d'URL qui peuvent être utilisés, vous pouvez consulter la documentation du NYTimes.

-
- -
-

Note : Dans cet exemple, la validation du formulaire est assez rudimentaire : le terme recherché doit nécessairement être renseigné avant de pouvoir valider le formulaire grâce à l'attribut required. Les champs pour les dates doivent suivre un format particulier et elles ne seront pas envoyées tant que leur valeur ne se composera pas de 8 chiffres (en HTML, c'est ce qui est indiqué par l'attribut pattern="[0-9]{8}"). Voir la page sur la validation des données d'un formulaire pour en savoir plus sur ce fonctionnement.

-
- -

Récupérer des données depuis l'API

- -

Maintenant que nous avons construit notre URL, envoyons une requête à cet endroit. Pour cela, nous utiliserons l'API Fetch.

- -

Dans la fonction fetchResults(), juste avant l'accolade de fin, ajoutez le fragment de code suivant :

- -
// On utilise fetch() pour envoyer la requête à l'API
-fetch(url).then(function(result) {
-  return result.json();
-}).then(function(json) {
-  displayResults(json);
-});
- -

On envoie la requête en passant la variable url comme paramètre à la fonction fetch() puis on convertit le corps de la réponse avec la fonction json() puis on passe le JSON ainsi obtenu à la fonction displayResults() afin que les données puissent être affichées dans l'interface utilisateur.

- -

Afficher les données

- -

Voyons maintenant comment afficher les données reçues. Dans le fichier contenant votre code, ajoutez la fonction suivante après la fonction fetchResults().

- -
function displayResults(json) {
-  while (section.firstChild) {
-      section.removeChild(section.firstChild);
-  }
-
-  var articles = json.response.docs;
-
-  if(articles.length === 10) {
-    nav.style.display = 'block';
-  } else {
-    nav.style.display = 'none';
-  }
-
-  if(articles.length === 0) {
-    var para = document.createElement('p');
-    para.textContent = 'Aucun résultat reçu.'
-    section.appendChild(para);
-  } else {
-    for(var i = 0; i < articles.length; i++) {
-      var article = document.createElement('article');
-      var heading = document.createElement('h2');
-      var link = document.createElement('a');
-      var img = document.createElement('img');
-      var para1 = document.createElement('p');
-      var para2 = document.createElement('p');
-      var clearfix = document.createElement('div');
-
-      var current = articles[i];
-      console.log(current);
-
-      link.href = current.web_url;
-      link.textContent = current.headline.main;
-      para1.textContent = current.snippet;
-      para2.textContent = 'Mots-clés : ';
-      for(var j = 0; j < current.keywords.length; j++) {
-        var span = document.createElement('span');
-        span.textContent += current.keywords[j].value + ' ';
-        para2.appendChild(span);
-      }
-
-      if(current.multimedia.length > 0) {
-        img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
-        img.alt = current.headline.main;
-      }
-
-      clearfix.setAttribute('class','clearfix');
-
-      article.appendChild(heading);
-      heading.appendChild(link);
-      article.appendChild(img);
-      article.appendChild(para1);
-      article.appendChild(para2);
-      article.appendChild(clearfix);
-      section.appendChild(article);
-    }
-  }
-}
- -

Il y a pas mal de code ici. Reprenons étape par étape pour l'expliquer :

- - - -

Câbler les boutons pour la pagination

- -

Pour que les boutons de pagination fonctionnent, on incrémente (ou on décrémente) la valeur de la variable pageNumber puis on renvoie une requête avec la nouvelle valeur incluse dans le paramètre de l'URL page. Cela fonctionne car l'API du NYTimes ne renvoie que 10 résultats à la fois. S'il y a plus de 10 résultats disponibles, la requête renverra les 10 premiers (0 à 9) lorsque le paramètre page vaut 0 dans l'URL (ou lorsqu'il n'est pas inclus du tout, c'est la valeur par défaut). Les 10 prochains résultats (10 à 19) peuvent être récupérés lorsque le paramètre page vaut 1 et ainsi de suite.

- -

En connaissant cela, on peut écrire une fonction pour gérer la pagination.

- -
    -
  1. -

    Après l'appel existant à addEventListener(), on ajoute les deux prochaines lignes qui appelleront les fonctions nextPage() et previousPage() lorsqu'on cliquera sur le bouton correspondant :

    - -
    nextBtn.addEventListener('click', nextPage);
    -previousBtn.addEventListener('click', previousPage);
    -
  2. -
  3. -

    À la suite, on définit le corps de ces fonctions :

    - -
    function nextPage(e) {
    -  pageNumber++;
    -  fetchResults(e);
    -};
    -
    -function previousPage(e) {
    -  if(pageNumber > 0) {
    -    pageNumber--;
    -  } else {
    -    return;
    -  }
    -  fetchResults(e);
    -};
    - -

    La première fonction est assez simple : on incrémente la variable pageNumber puis on exécute à nouveau la fonction fetchResults() afin d'afficher les prochains résultats.

    - -

    La seconde fonction est similaire, mais on prend la précaution de vérifier que pageNumber ne vaut pas déjà 0 avant de diminuer sa valeur (si la requête est envoyée avec un paramètre négatif, on pourrait avoir une erreur). Lorsque pageNumber vaut déjà 0, on sort de la fonction avec return afin d'éviter de lancer une requête pour rien (si on est déjà sur la première page, pas besoin de recharger les résultats à nouveau).

    -
  4. -
- -
-

Note : Vous pouvez trouver l'exemple complet sur l'API du NYTimes sur GitHub.

-
- -

Exemple d'utilisation de l'API YouTube

- -

Nous avons également mis à disposition un autre exemple que vous pouvez étudier et adapter : exemple de recherche de vidéo YouTube. Dans cet exemple, on utilise deux API :

- - - -

Cet exemple est intéressant car il permet d'illustrer l'utilisation combinée de deux API tierces pour construire une application. La première API est une API REST tandis que la seconde est plus proche du fonctionnement de MapQuest (des méthodes spécifiques à l'API, etc.). On notera que les deux API ont besoin qu'une bibliothèque JavaScript soit chargée sur la page. L'API REST possède des fonctions qui permettent de gérer les requêtes HTTP et de renvoyer les résultats.

- -

- -

Nous n'allons pas détailler plus encore cet exemple ici, vous pouvez consulter le code source qui contient des commentaires expliquant son fonctionnement. Là encore, pour utiliser vous-même l'exemple, vous aurez besoin de récupérer une clé d'API et de l'insérer dans le code afin que les exemples fonctionnent.

- -

Résumé

- -

Dans cet article, nous avons vu une introduction à l'utilisation des API tierces afin d'ajouter des fonctionnalités à nos sites web.

- -

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.md b/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.md new file mode 100644 index 0000000000..15dfb1c063 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/third_party_apis/index.md @@ -0,0 +1,441 @@ +--- +title: API tierces +slug: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs +tags: + - API + - Apprendre + - Débutant +translation_of: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Third_party_APIs +--- +
{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

Jusqu'à présent, nous avons uniquement abordé des API qui sont fournies par le navigateur. Il en existe d'autres : de nombreux sites et services, tels que Google Maps, Twitter, Facebook, PayPal, etc. fournissent des API permettant aux développeurs d'exploiter leurs données (ex. afficher un flux Twitter sur un blog) ou leurs services (utiliser l'outil de connexion Facebook pour que vos utilisateurs se connectent sur votre application). Dans cet article, nous verrons les différences entre les API du navigateur et celles fournies par des services tiers (en anglais, on parle de « third-party API ») et nous illustrerons certains cas d'usage pour ces API tierces.

+ + + + + + + + + + + + +
Prérequis :Les bases de JavaScript (voir premiers pas, blocs de construction, les objets JavaScript), les bases des API côté client.
Objectifs :Comprendre le fonctionnement des API tierces et comment les utiliser pour ajouter des fonctionnalités à ses sites / applications.
+ +

Qu'est-ce qu'une API tierce ?

+ +

Les API tierces sont des API qui sont fournis par des tiers, généralement des entreprises comme Facebook, Twitter ou Google, qui permettent d'accéder à leurs données et/ou leurs fonctionnalités grâce à JavaScript afin de les utiliser sur son site. Utiliser une API de cartographie afin d'afficher une carte sur une page est un exemple.

+ +

Regardons cet exemple qui utilise l'API MapQuest et voyons avec lui les différences entre les API tierces et celles du navigateur.

+ +
+

Note : Vous pouvez récupérer l'ensemble des exemples de code en une seule fois. Dans ce cas, il vous suffit de rechercher dans votre dépôt les fichiers utilisés pour chaque section.

+
+ +

Elles sont situées sur des serveurs tiers

+ +

Les API fournies par le navigateur sont construites dans le navigateur et on peut y accéder immédiatement avec du code JavaScript. Ainsi, l'API Web Audio que nous avons vu dans cet article introductif s'utilise via l'objet {{domxref("AudioContext")}} fourni nativement :

+ +
const audioCtx = new AudioContext();
+  ...
+const audioElement = document.querySelector('audio');
+  ...
+const audioSource = audioCtx.createMediaElementSource(audioElement);
+// etc.
+ +

En revanche, les API tierces sont situées sur des serveurs tiers. Pour y accéder avec JavaScript, il faut d'abord se connecter aux fonctionnalités de l'API afin de les rendre disponibles sur la page. Pour cela, on pourra utiliser une bibliothèque JavaScript disponible sur le serveur via un élément {{htmlelement("script")}}. Pour notre exemple avec MapQuest, voici ce que ça donne :

+ +
<script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"></script>
+<link type="text/css" rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"/>
+ +

On peut alors utiliser les objets fournis par cette bibliothèque. Voici un fragment de code qui illustre cette utilisation :

+ +
var L;
+
+var map = L.mapquest.map('map', {
+  center: [53.480759, -2.242631],
+  layers: L.mapquest.tileLayer('map'),
+  zoom: 12
+});
+ +

Ici on crée une variable dans laquelle enregistrer les informations de la carte puis on crée une nouvelle carte à l'aide de la méthode mapquest.map() qui prend comme argument :

+ + + +

C'est tout ce dont l'API MapQuest a besoin pour dessiner une carte. C'est le serveur auquel on se connecte qui gère les aspects plus compliqués (comme afficher les bonnes tuiles pour la zone géographique, etc.).

+ +
+

Note : Certaines API fonctionnent différemment pour l'accès aux fonctionnalités et passent par une requête HTTP sur une URL spécifique pour récupérer les données. Ces API sont appelées API REST (ou RESTful APIs en anglais) et nous les abordons plus bas dans l'article.

+
+ +

Des clés d'API sont nécessaires pour les utiliser

+ +

Dans les navigateurs, comme nous l'avons vu dans le premier article, les sécurités relatives aux API sont gérées via des permissions afin d'avertir l'utilisateur et d'éviter les utilisations malveillantes de la part des sites.

+ +

Pour les API tierces, le système est légèrement différent. Généralement, ce sont des clés qui sont utilisées afin de fournir aux développeurs l'accès aux fonctionnalités de l'API. Dans ce système, la clé sert plutôt à protéger des abus de la part des développeurs envers le site tiers.

+ +

Dans l'exemple MapQuest, vous pourrez trouver une ligne semblable à celle-ci :

+ +
L.mapquest.key = 'VOTRE-CLE-D-API-ICI';
+ +

Cette ligne indique une clé d'API utilisée par notre application. Le développeur de l'application doit obtenir une clé et l'inclure dans le code de l'application afin de pouvoir accéder aux fonctionnalités de l'API. Pour cet exemple, il s'agit d'une valeur imaginaire.

+ +
+

Note : Lorsque vous construisez votre propre application, utilisez une « vraie » valeur de clé plutôt que la valeur de substitution fournie en exemple.

+
+ +

Certaines API peuvent nécessiter de fournir la clé d'une façon différente mais le fonctionnement général reste similaire.

+ +

L'existence d'une telle clé d'API permet au fournisseur tiers de contrôler les accès et actions des différents développeurs/utilisateurs. Ainsi, lorsqu'un développeur demande une clé, il devient alors connu du fournisseur de l'API et ce dernier peut agir de son côté si l'API est détournée ou utilisée de façon incorrecte (par exemple pour pister des personnes ou parce que le site du développeur sollicite l'API avec de trop nombreuses requêtes). Dès qu'un usage incorrect est détecté du côté du fournisseur, il suffit alors de révoquer l'accès et de couper ou de limiter les accès pour cette clé.

+ +

Étendre l'exemple « MapQuest »

+ +

Ajoutons quelques fonctionnalités à cet exemple MapQuest afin d'illustrer le fonctionnement d'autres aspects de l'API.

+ +
    +
  1. +

    Pour commencer cette section, copiez le fichier initial dans un nouveau répertoire. Si vous avez déjà cloné le dépôt des exemples, vous disposez déjà d'une copie située sous le répertoire javascript/apis/third-party-apis/mapquest.

    +
  2. +
  3. +

    Ensuite, rendez-vous sur le site MapQuest pour les développeurs, créez un compte puis créez une clé de développement (au moment où nous écrivons ces lignes, sur le site, le nom utilisé est "consumer key" et la procédure de création demande aussi la saisie d'une URL "callback URL" qui est optionnelle (vous pouvez la laisser vide).

    +
  4. +
  5. Ouvrez un éditeur pour éditer le fichier initial et remplacez la valeur pour la clé d'API avec la vôtre.
  6. +
+ +

Modifier le type de la carte

+ +

L'API MapQuest permet d'afficher différents types de carte. Localisez la ligne suivante dans votre fichier :

+ +
layers: L.mapquest.tileLayer('map')
+ +

Ici, vous pouvez changer 'map' en 'hybrid' afin d'afficher une carte avec un style hybride. Vous pouvez essayer d'autres valeurs comme celles indiquées sur la page de référence de la méthode tileLayer().

+ +

Ajouter différents contrôles

+ +

Sur la carte, on peut utiliser différents contrôles. Par défaut, seul un contrôle pour le zoom est affiché. Il est possible d'étendre les contrôles disponibles en utilisant la méthodemap.addControl(). Ajoutez la ligne suivante à l'intérieur du gestionnaire window.onload :

+ +
map.addControl(L.mapquest.control());
+ +

La méthode mapquest.control() crée un ensemble complet de contrôle et est placée, par défaut, dans le coin supérieur droit de la carte. Cette position peut être ajustée grâce à un paramètre d'options contenant une propriété position dont la valeur est un mot-clé décrivant la position souhaitée. Vous pouvez modifier la ligne de la façon suivante par exemple :

+ +
  map.addControl(L.mapquest.control({ position: 'bottomright' }));
+ +

D'autres types de contrôle sont disponibles comme mapquest.searchControl() et mapquest.satelliteControl(). Certains sont avancés et permettent de nombreuses choses. N'hésitez pas à en essayer différents pour voir ce qui vous semble le mieux pour votre projet.

+ +

Ajouter un marqueur personnalisé

+ +

Pour ajouter une icône sur un point de la carte, on pourra utiliser la méthode L.marker()  (documentée dans la documentation de Leaflet.js). Dans window.onload, vous pouvez ajouter le fragment de code suivante window.onload :

+ +
L.marker([53.480759, -2.242631], {
+  icon: L.mapquest.icons.marker({
+    primaryColor: '#22407F',
+    secondaryColor: '#3B5998',
+    shadow: true,
+    size: 'md',
+    symbol: 'A'
+  })
+})
+.bindPopup('Ici se trouve Manchester !')
+.addTo(map);
+ +

Comme on peut le voir ici, la méthode peut prendre deux paramètres :

+ + + +

L'icône est définie via un appel à la méthode mapquest.icons.marker() à laquelle on fournit des informations telles que la couleur et la taille du marqueur.

+ +

Après l'appel à la première méthode, on enchaîne avec un appel avec .bindPopup('Ici se trouve Manchester !'), qui fournit le contenu à afficher lorsqu'on cliquera sur le marqueur.

+ +

Enfin, on chaîne un appel .addTo(map) pour ajouter le marqueur sur la carte.

+ +

Essayez les différentes options que vous trouverez dans la documentation et voyez quel résultat vous pouvez obtenir ! MapQuest fournit certaines fonctionnalités relativement avancées (itinéraire, recherche, etc.).

+ +
+

Note : Si vous ne parvenez pas à faire fonctionner l'exemple, vous pouvez consulter la version finalisée de notre exemple : expanded-example.html.

+
+ +

Quid de Google Maps ?

+ +

Google Maps est sans doute l'API de cartographie la plus populaire. Pourquoi ne l'avons-nous pas incluse ici ? Nous avons choisi MapQuest pour plusieurs raisons :

+ + + +

Une approche pour utiliser les API tierces

+ +

Une API REST du NYTimes

+ +

Prenons un autre exemple d'API, l'API du New York Times. Cette API permet de récupérer les informations provenant du New York Times et de les afficher sur votre site. Cette API est ce qu'on appelle une API REST car elle permet de récupérer des données via des requêtes HTTP sur des URL spécifiques dans lesquelles on peut fournir des données comme des termes de recherches (souvent encodés comme paramètres dans l'URL). Ce type d'API est relativement fréquent.

+ +

Construisons un second exemple pour comprendre comment utiliser l'API du NYTimes. Au fur et à mesure nous décrirons également une approche plus générale pour consommer d'autres API tierces.

+ +

Trouver la documentation

+ +

Lorsqu'on veut utiliser une API tierce, il est toujours utile de trouver la documentation correspondante pour connaître les fonctionnalités offertes, comment les utiliser, etc. La documentation de l'API du New York Times API se situe ici : https://developer.nytimes.com/.

+ +

Obtenir une clé de développement

+ +

La plupart des API reposent sur l'obtention et l'utilisation d'une clé de développement (tant pour des raisons de sécurité que de responsabilité). Pour obtenir une clé de développement pour l'API du NYTimes, vous pouvez suivre les instructions de https://developer.nytimes.com/get-started.

+ +
    +
  1. +

    Demandez une clé pour l'API Article Search — créez une nouvelle application et sélectionnez cette API, fournissez un nom et une description pour votre application, activez le bouton sous "Article Search API" puis cliquez sur "Create").

    +
  2. +
  3. +

    Vous pouvez alors récupérer la clé d'API à partir de la page suivante.

    +
  4. +
  5. +

    Pour construire le socle de notre exemple, copiez nytimes_start.html et nytimes.css dans un nouveau répertoire sur votre ordinateur. Si vous avez déjà cloné le dépôt des exemples, vous disposez déjà d'un exemplaire de ces fichiers et vous pourrez les trouver sous le répertoire javascript/apis/third-party-apis/nytimes. L'élément <script> contient un certain nombre de variables nécessaires à l'initialisation de l'exemple. Nous allons ensuite remplir les fonctionnalités nécessaires.

    +
  6. +
+ +

Au final, on souhaite que l'application permette de saisir un terme de recherche, des dates optionnelles pour le début et la fin de la période à rechercher. Nous utiliserons alors ces paramètres afin de d'envoyer des requêtes sur l'API Article Search puis nous afficherons les résultats obtenus.

+ +

+ +

Connecter l'API à son application

+ +

Tout d'abord, vous devrez créer une connexion entre l'API et votre application. Pour cette API, vous devez fournir la clé d'API comme paramètre GET à chaque requête.

+ +
    +
  1. +

    Localisez la ligne qui suit et remplacez la valeur avec la clé de développement que vous avez obtenu plus tôt :

    + +
    var key = ' ... ';
    +
  2. +
  3. +

    Ajoutez la ligne suivante sous le commentaire // Event listeners to control the functionality. Cette ligne permet d'exécuter la fonction submitSearch() lorsque le formulaire est envoyé (quand on presse le bouton).

    + +
    searchForm.addEventListener('submit', submitSearch);
    +
  4. +
  5. +

    Sous cette nouvelle ligne, ajoutons les fonctions submitSearch() et fetchResults() :

    + +
    function submitSearch(e) {
    +  pageNumber = 0;
    +  fetchResults(e);
    +}
    +
    +function fetchResults(e) {
    +  // On utilise preventDefault() afin d'éviter
    +  // l'envoie vers notre serveur et le rafraîchissement
    +  // de la page
    +  e.preventDefault();
    +
    +  // On compose l'URL
    +  url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
    +
    +  if(startDate.value !== '') {
    +    url += '&begin_date=' + startDate.value;
    +  };
    +
    +  if(endDate.value !== '') {
    +    url += '&end_date=' + endDate.value;
    +  };
    +
    +}
    +
  6. +
+ +

submitSearch() commence par réinitialiser le nombre de page à 0 puis appelle fetchResults(). Cette fonction commence par appeler preventDefault() sur l'évènement afin d'empêcher l'envoi du formulaire vers notre serveur (ce qui casserait l'exemple). Ensuite, on assemble la chaîne de caractères qui formera l'URL sur laquelle on fera la requête. Dans cette URL, on commence par mettre les fragments « obligatoires » (en tout cas pour cette démonstration) :

+ + + +

Après, on utilise un ensemble d'instructions if() pour vérifier si des valeurs ont été fournies pour les champs startDate et endDate dans le formulaire. Si c'est le cas, on utilise ces valeurs pour renseigner les paramètres d'URL begin_date et/ou end_date.

+ +

Si on prend un exemple d'URL complète ainsi construite :

+ +
https://api.nytimes.com/svc/search/v2/articlesearch.json?api-key=YOUR-API-KEY-HERE&page=0&q=cats
+&fq=document_type:("article")&begin_date=20170301&end_date=20170312 
+ +
+

Note : Pour en savoir plus sur les différents paramètres d'URL qui peuvent être utilisés, vous pouvez consulter la documentation du NYTimes.

+
+ +
+

Note : Dans cet exemple, la validation du formulaire est assez rudimentaire : le terme recherché doit nécessairement être renseigné avant de pouvoir valider le formulaire grâce à l'attribut required. Les champs pour les dates doivent suivre un format particulier et elles ne seront pas envoyées tant que leur valeur ne se composera pas de 8 chiffres (en HTML, c'est ce qui est indiqué par l'attribut pattern="[0-9]{8}"). Voir la page sur la validation des données d'un formulaire pour en savoir plus sur ce fonctionnement.

+
+ +

Récupérer des données depuis l'API

+ +

Maintenant que nous avons construit notre URL, envoyons une requête à cet endroit. Pour cela, nous utiliserons l'API Fetch.

+ +

Dans la fonction fetchResults(), juste avant l'accolade de fin, ajoutez le fragment de code suivant :

+ +
// On utilise fetch() pour envoyer la requête à l'API
+fetch(url).then(function(result) {
+  return result.json();
+}).then(function(json) {
+  displayResults(json);
+});
+ +

On envoie la requête en passant la variable url comme paramètre à la fonction fetch() puis on convertit le corps de la réponse avec la fonction json() puis on passe le JSON ainsi obtenu à la fonction displayResults() afin que les données puissent être affichées dans l'interface utilisateur.

+ +

Afficher les données

+ +

Voyons maintenant comment afficher les données reçues. Dans le fichier contenant votre code, ajoutez la fonction suivante après la fonction fetchResults().

+ +
function displayResults(json) {
+  while (section.firstChild) {
+      section.removeChild(section.firstChild);
+  }
+
+  var articles = json.response.docs;
+
+  if(articles.length === 10) {
+    nav.style.display = 'block';
+  } else {
+    nav.style.display = 'none';
+  }
+
+  if(articles.length === 0) {
+    var para = document.createElement('p');
+    para.textContent = 'Aucun résultat reçu.'
+    section.appendChild(para);
+  } else {
+    for(var i = 0; i < articles.length; i++) {
+      var article = document.createElement('article');
+      var heading = document.createElement('h2');
+      var link = document.createElement('a');
+      var img = document.createElement('img');
+      var para1 = document.createElement('p');
+      var para2 = document.createElement('p');
+      var clearfix = document.createElement('div');
+
+      var current = articles[i];
+      console.log(current);
+
+      link.href = current.web_url;
+      link.textContent = current.headline.main;
+      para1.textContent = current.snippet;
+      para2.textContent = 'Mots-clés : ';
+      for(var j = 0; j < current.keywords.length; j++) {
+        var span = document.createElement('span');
+        span.textContent += current.keywords[j].value + ' ';
+        para2.appendChild(span);
+      }
+
+      if(current.multimedia.length > 0) {
+        img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
+        img.alt = current.headline.main;
+      }
+
+      clearfix.setAttribute('class','clearfix');
+
+      article.appendChild(heading);
+      heading.appendChild(link);
+      article.appendChild(img);
+      article.appendChild(para1);
+      article.appendChild(para2);
+      article.appendChild(clearfix);
+      section.appendChild(article);
+    }
+  }
+}
+ +

Il y a pas mal de code ici. Reprenons étape par étape pour l'expliquer :

+ + + +

Câbler les boutons pour la pagination

+ +

Pour que les boutons de pagination fonctionnent, on incrémente (ou on décrémente) la valeur de la variable pageNumber puis on renvoie une requête avec la nouvelle valeur incluse dans le paramètre de l'URL page. Cela fonctionne car l'API du NYTimes ne renvoie que 10 résultats à la fois. S'il y a plus de 10 résultats disponibles, la requête renverra les 10 premiers (0 à 9) lorsque le paramètre page vaut 0 dans l'URL (ou lorsqu'il n'est pas inclus du tout, c'est la valeur par défaut). Les 10 prochains résultats (10 à 19) peuvent être récupérés lorsque le paramètre page vaut 1 et ainsi de suite.

+ +

En connaissant cela, on peut écrire une fonction pour gérer la pagination.

+ +
    +
  1. +

    Après l'appel existant à addEventListener(), on ajoute les deux prochaines lignes qui appelleront les fonctions nextPage() et previousPage() lorsqu'on cliquera sur le bouton correspondant :

    + +
    nextBtn.addEventListener('click', nextPage);
    +previousBtn.addEventListener('click', previousPage);
    +
  2. +
  3. +

    À la suite, on définit le corps de ces fonctions :

    + +
    function nextPage(e) {
    +  pageNumber++;
    +  fetchResults(e);
    +};
    +
    +function previousPage(e) {
    +  if(pageNumber > 0) {
    +    pageNumber--;
    +  } else {
    +    return;
    +  }
    +  fetchResults(e);
    +};
    + +

    La première fonction est assez simple : on incrémente la variable pageNumber puis on exécute à nouveau la fonction fetchResults() afin d'afficher les prochains résultats.

    + +

    La seconde fonction est similaire, mais on prend la précaution de vérifier que pageNumber ne vaut pas déjà 0 avant de diminuer sa valeur (si la requête est envoyée avec un paramètre négatif, on pourrait avoir une erreur). Lorsque pageNumber vaut déjà 0, on sort de la fonction avec return afin d'éviter de lancer une requête pour rien (si on est déjà sur la première page, pas besoin de recharger les résultats à nouveau).

    +
  4. +
+ +
+

Note : Vous pouvez trouver l'exemple complet sur l'API du NYTimes sur GitHub.

+
+ +

Exemple d'utilisation de l'API YouTube

+ +

Nous avons également mis à disposition un autre exemple que vous pouvez étudier et adapter : exemple de recherche de vidéo YouTube. Dans cet exemple, on utilise deux API :

+ + + +

Cet exemple est intéressant car il permet d'illustrer l'utilisation combinée de deux API tierces pour construire une application. La première API est une API REST tandis que la seconde est plus proche du fonctionnement de MapQuest (des méthodes spécifiques à l'API, etc.). On notera que les deux API ont besoin qu'une bibliothèque JavaScript soit chargée sur la page. L'API REST possède des fonctions qui permettent de gérer les requêtes HTTP et de renvoyer les résultats.

+ +

+ +

Nous n'allons pas détailler plus encore cet exemple ici, vous pouvez consulter le code source qui contient des commentaires expliquant son fonctionnement. Là encore, pour utiliser vous-même l'exemple, vous aurez besoin de récupérer une clé d'API et de l'insérer dans le code afin que les exemples fonctionnent.

+ +

Résumé

+ +

Dans cet article, nous avons vu une introduction à l'utilisation des API tierces afin d'ajouter des fonctionnalités à nos sites web.

+ +

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html b/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html deleted file mode 100644 index a091835f18..0000000000 --- a/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html +++ /dev/null @@ -1,519 +0,0 @@ ---- -title: APIs vidéo et audio -slug: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs -tags: - - API - - Apprendre - - Article - - Audio - - Codage - - Débutant - - Guide - - JavaScript - - Video -translation_of: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs -original_slug: Apprendre/JavaScript/Client-side_web_APIs/Video_and_audio_APIs ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}
- -

HTML5 fournit des éléments pour intégrer du multimédia dans les documents — {{htmlelement("video")}} et {{htmlelement("audio")}} — et qui viennent avec leurs propres APIs pour contrôler la lecture, se déplacer dans le flux, etcCet article montre comment réaliser les tâches les plus communes, comme créer des contrôles de lectures personnalisés.

- - - - - - - - - - - - -
Prérequis:Les bases du JavaScript (voir premiers pas en JavaScript, les briques Javascript, Introduction aux objets), Introduction aux APIs web
Objectif:Apprendre à utiliser les APIs du navigateur pour contrôler la lecture de audio et vidéo.
- -

Les balises HTML5 video et audio

- -

Les balises {{htmlelement("video")}} et {{htmlelement("audio")}} permettent d'intégrer des vidéos et de l'audio dans des pages web. Comme nous l'avons montré dans Contenu audio et vidéo, une implémentation habituelle ressemble à ça :

- -
<video controls>
-  <source src="rabbit320.mp4" type="video/mp4">
-  <source src="rabbit320.webm" type="video/webm">
-  <p>Votre navigateur ne supporte pas la vidéo HTML5. Voici à la place <a href="rabbit320.mp4">un lien vers la vidéo</a>.</p>
-</video>
- -

Cela crée un lecteur vidéo à l'intérieur du navigateur:

- -

{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html", '100%', 380)}}

- -

Vous pouvez consulter toutes fonctionnalités HTML audio et vidéo dans l'article mentionné précédemment. Pour notre utilisation ici, l'attribut le plus intéressant est {{htmlattrxref("controls", "video")}}. Il permet d'activer l'ensemble des contrôles de lecture par défaut; si vous ne le spécifiez pas, vous aucun contrôle ne sera affiché:

- -

{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html", '100%', 380)}}

- -

Ce n'est pas immédiatement utile pour la lecture de vidéos, mais ça a des avantages. Les contrôles natifs des navigateurs différent complètement d'un navigateur à l'autre — ce qui est embêtant pour un support global des différents navigateurs. Un autre problème est que le contrôles natifs sont généralement assez peu accessibles au clavier.

- -

Vous pouvez régler ces deux problèmes en cachant les contrôles natifs (en supprimant l'attribut controls) et en les remplaçant par les votres en HTML, CSS et JavaScript. Dans la prochaine section, nous verrons les outils de base à notre disposition pour faire ça.

- -

L'API HTMLMediaElement

- -

L'API {{domxref("HTMLMediaElement")}}, spécifiée dans HTML5, fournit des fonctionnalités qui permettent de controller des lecteurs audio et vidéo avec JavaScript — avec par exemple {{domxref("HTMLMediaElement.play()")}} ou encore {{domxref("HTMLMediaElement.pause()")}}. Cette interface est disponible à la fois pour les balises {{htmlelement("audio")}} et {{htmlelement("video")}}, les fonctionnalités utiles pour les deux étant quasiment identiques. Voyons un exemple pour découvrir ces fonctionnalités.

- -

Notre exemple final ressemblera (et fonctionnera) comme ceci:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/video-audio/finished/", '100%', 360)}}

- -

Débuter

- -

Pour commencer avec cet exemple, télechargez notre media-player-start.zip et décompressez-le dans un nouveau dossier sur votre disque dur. Si vous avez téléchargé notre dépôt d'exemples, vous le trouverez dans javascript/apis/video-audio/start/.

- -

Si vous ouvrez la page HTML, vous devriez voir un lecteur HTML5 utilisant les contrôles natifs.

- -

Exploration du HTML

- -

Ouvrez le fichier HTML d'index. Vous allez voir que le HTML contient majoritairement du code pour le lecteur et ses contrôles:

- -
<div class="player">
-  <video controls>
-    <source src="video/sintel-short.mp4" type="video/mp4">
-    <source src="video/sintel-short.mp4" type="video/webm">
-    <!-- fallback contenu ici -->
-  </video>
-  <div class="controls">
-    <button class="play" data-icon="P" aria-label="bascule lecture pause"></button>
-    <button class="stop" data-icon="S" aria-label="stop"></button>
-    <div class="timer">
-      <div></div>
-      <span aria-label="timer">00:00</span>
-    </div>
-    <button class="rwd" data-icon="B" aria-label="retour arrière"></button>
-    <button class="fwd" data-icon="F" aria-label="avance rapide"></button>
-  </div>
-</div>
-
- - - -

Exploration du CSS

- -

Maintenant, ouvrez le fichier CSS et jetez-y un coup d'oeil. Le CSS pour cet exemple n'est pas très compliqué, mais nous allons voir les éléments les plus intéressants ici. Tout d'abord, le style de .controls:

- -
.controls {
-  visibility: hidden;
-  opacity: 0.5;
-  width: 400px;
-  border-radius: 10px;
-  position: absolute;
-  bottom: 20px;
-  left: 50%;
-  margin-left: -200px;
-  background-color: black;
-  box-shadow: 3px 3px 5px black;
-  transition: 1s all;
-  display: flex;
-}
-
-.player:hover .controls, player:focus .controls {
-  opacity: 1;
-}
-
- - - -

Ensuite, voyons les icônes des boutons:

- -
@font-face {
-   font-family: 'HeydingsControlsRegular';
-   src: url('fonts/heydings_controls-webfont.eot');
-   src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
-        url('fonts/heydings_controls-webfont.woff') format('woff'),
-        url('fonts/heydings_controls-webfont.ttf') format('truetype');
-   font-weight: normal;
-   font-style: normal;
-}
-
-button:before {
-  font-family: HeydingsControlsRegular;
-  font-size: 20px;
-  position: relative;
-  content: attr(data-icon);
-  color: #aaa;
-  text-shadow: 1px 1px 0px black;
-}
- -

Tout d'abord, en haut du CSS, nous utilisons un bloc {{cssxref("@font-face")}} pour importer une police web personnalisée. Il s'agit d'une police d'icônes — tous les caractères de l'alphabet correspondent à des icônes que vous pouvez utiliser dans votre application.

- -

Ensuite, nous générons du contenu pour afficher une icône sur chaque bouton:

- - - -

Les polices d'icônes sont très cool pour de nombreuses raisons: réduire les requêtes HTTP (puisque vous n'avez pas besoin de télécharger des icônes sous forme de fichiers image), bonne scalabilité, et le fait que vous pouvez utiliser les propriétés de texte pour les formatter — comme {{cssxref("color")}} et {{cssxref("text-shadow")}}.

- -

Dernier point mais non des moindres, le CSS du décompte:

- -
.timer {
-  line-height: 38px;
-  font-size: 10px;
-  font-family: monospace;
-  text-shadow: 1px 1px 0px black;
-  color: white;
-  flex: 5;
-  position: relative;
-}
-
-.timer div {
-  position: absolute;
-  background-color: rgba(255,255,255,0.2);
-  left: 0;
-  top: 0;
-  width: 0;
-  height: 38px;
-  z-index: 2;
-}
-
-.timer span {
-  position: absolute;
-  z-index: 3;
-  left: 19px;
-}
- - - -

Implémenter le JavaScript

- -

Nous avons déjà une interface HTML et CSS assez complète; nous avons maintenant besoin de gérer les boutons pour que les contrôles fonctionnent.

- -
    -
  1. -

    Créez un nouveau fichier JavaScript dans le même répertoire que votre fichier index.html. Nous l'appelerons custom-player.js.

    -
  2. -
  3. -

    En haut de ce fichier, insérez le code suivant:

    - -
    var media = document.querySelector('video');
    -var controls = document.querySelector('.controls');
    -
    -var play = document.querySelector('.play');
    -var stop = document.querySelector('.stop');
    -var rwd = document.querySelector('.rwd');
    -var fwd = document.querySelector('.fwd');
    -
    -var timerWrapper = document.querySelector('.timer');
    -var timer = document.querySelector('.timer span');
    -var timerBar = document.querySelector('.timer div');
    -
    - -

    Ici, nous créons des variables pour stocker les références de tous les objets que nous voulons manipuler. Nous avons trois groupes:

    - -
      -
    • L'élément <video>, et la barre de contrôle.
    • -
    • Les boutons play/pause, stop, retour arrière, et avance rapide.
    • -
    • Le <div> externe, le <span> qui décompte le temps écoulé, et le <div> interne qui affiche le progrès de la vidéo.
    • -
    -
  4. -
  5. -

    Ensuite, insérez ce qui suit en bas de votre code:

    - -
    media.removeAttribute('controls');
    -controls.style.visibility = 'visible';
    - -

    Ces deux lignes suppriment les contrôles par défaut du navigateur sur la vidéo, et rendent nos contrôles personnalisés visibles.

    -
  6. -
- -

Lecture et pause de la vidéo

- -

Imlémentons le contrôle le plus important — le bouton play/pause.

- -
    -
  1. -

    Tout d'abord, ajoutez ce qui suit au bas de votre code, pour que la fonction playPauseMedia() soit invoquée quand le bouton play est cliqué:

    - -
    play.addEventListener('click', playPauseMedia);
    -
    -
  2. -
  3. -

    Maintenant, définissons playPauseMedia() — ajoutez ce qui suit, toujours au bas de votre code:

    - -
    function playPauseMedia() {
    -  if(media.paused) {
    -    play.setAttribute('data-icon','u');
    -    media.play();
    -  } else {
    -    play.setAttribute('data-icon','P');
    -    media.pause();
    -  }
    -}
    - -

    Ici, nous utilisons une instruction if pour vérifier si la vidéo est en pause. La propriété {{domxref("HTMLMediaElement.paused")}} retourne vrai si le média est en pause — c'est le cas quand la vidéo n'est pas en cours de lecture, y compris quand la vidéo est au début après son chargement. Si elle est en pause, nous définissons la valeur de l'attribut data-icon à "u", qui est une icône "en pause", et invoquons la  méthode {{domxref("HTMLMediaElement.play()")}} pour jouer le média.

    - -

    Au second clic, le bouton sera de nouveau alterné — l'icône "play" sera affiché, et la vidéo sera mise en pause avec {{domxref("HTMLMediaElement.paused()")}}.

    -
  4. -
- -

Stopper la vidéo

- -
    -
  1. -

    Ajoutons la possibilité d'arrêter la vidéo. Ajoutez les lignes addEventListener() suivantes au-dessous de vos ajouts précédents:

    - -
    stop.addEventListener('click', stopMedia);
    -media.addEventListener('ended', stopMedia);
    -
    - -

    L'événement {{event("click")}} est explicite — nous voulons stopper la vidéo en appelant la fonction stopMedia() quand le bouton stop est cliqué. Cependant, nous voulons également stopper la vidéo quand elle a fini de jouer — signalé par l'événement {{event("ended")}}, nous pouvons donc mettre en place un gestionnaire d'événement pour exécuter la fonction quand cet événément se produit.

    -
  2. -
  3. -

    Ensuite, définissons stopMedia() — ajoutez ce qui suit après la fonction playPauseMedia():

    - -
    function stopMedia() {
    -  media.pause();
    -  media.currentTime = 0;
    -  play.setAttribute('data-icon','P');
    -}
    -
    - -

    Il n'y a pas de méthode stop() dans l'API  HTMLMediaElement — l'équivalent du stop est de mettre pause() sur la vidéo, et de définir la propriété {{domxref("HTMLMediaElement.currentTime","currentTime")}} à 0. Définir une valeur à currentTime (en secondes) change immédiatement la position du temps du média.

    - -

    Tout ce qui nous reste à faire après ça est d'afficher l'icône "play". Que la vidéo ait été en train de jouer ou en pause, quand le bouton stop est pressé, vous voulez qu'elle doit prête à être lue.

    -
  4. -
- -

Retour arrière et avance rapide

- -

Il y a différentes manières d'implémenter le retour arrière et l'avance rapide; ici, nous vous montrons une manière relativement complexe de le faire, qui n'a pas de comportement inattendu quand différents boutons sont pressés dans un ordre aléatoire.

- -
    -
  1. -

    Tout d'abord, ajoutez les lignes addEventListener() suivantes à la suite des précédentes:

    - -
    rwd.addEventListener('click', mediaBackward);
    -fwd.addEventListener('click', mediaForward);
    -
    -
  2. -
  3. -

    Maintenant, occupons-nous des fonctions des gestionnaires d'événément — ajoutez le code suivant à la suite des fonctions précédentes pour définir mediaBackward() et mediaForward():

    - -
    var intervalFwd;
    -var intervalRwd;
    -
    -function mediaBackward() {
    -  clearInterval(intervalFwd);
    -  fwd.classList.remove('active');
    -
    -  if(rwd.classList.contains('active')) {
    -    rwd.classList.remove('active');
    -    clearInterval(intervalRwd);
    -    media.play();
    -  } else {
    -    rwd.classList.add('active');
    -    media.pause();
    -    intervalRwd = setInterval(windBackward, 200);
    -  }
    -}
    -
    -function mediaForward() {
    -  clearInterval(intervalRwd);
    -  rwd.classList.remove('active');
    -
    -  if(fwd.classList.contains('active')) {
    -    fwd.classList.remove('active');
    -    clearInterval(intervalFwd);
    -    media.play();
    -  } else {
    -    fwd.classList.add('active');
    -    media.pause();
    -    intervalFwd = setInterval(windForward, 200);
    -  }
    -}
    -
    - -

    Vous remarquerez que nous commençons par initialiser deux variables — intervalFwd et intervalRwd — vous verrez à quoi elles servent plus tard.

    - -

    Voyons pas à pas mediaBackward() (mediaForward() fait la même chose, mais dans l'autre sens):

    - -
      -
    1. Nous effaçons les classes et intervales qui sont définits sur la fonctionnalité d'avance rapide — de cette manière, si on presse le bouton rwd après avoir pressé le bouton fwd, on annule l'avance rapide et la remplaçons avec le retour arrière. Si on essayait de faire les deux à la fois, le lecteur échouerait.
    2. -
    3. Nous utilisons une instruction if pour vérifier si la classe active a été définie sur le bouton rwd, ce qui indique qu'il a déjà été pressé. La propriété {{domxref("classList")}} est une propriété plutôt pratique qui existe sur chaque élément — elle contient une liste de toutes les classes définies sur l'élément, ainsi que des méthodes pour en ajouter/supprimer, etc. Nous utilisons la méthode classList.contains() pour vérifier si la liste contient la classe active. Cela retourne un booléen true/false en résultat.
    4. -
    5. Si la classe active a été définie sur le bouton rwd, nous la supprimons avec classList.remove(), effaçons l'intervale qui a été définit sur le bouton quand il a été pressé (voir ci-dessous pour plus d'explication), et utilisons {{domxref("HTMLMediaElement.play()")}} pour annuler le retour arrière et démarrer la vidéo normalement.
    6. -
    7. -

      Sinon, nous ajoutons la classe active sur le bouton rwd avec classList.add(), mettons la vidéo en pause en utilisant {{domxref("HTMLMediaElement.pause()")}}, puis définissons la variable intervalRwd en appelant {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}. Quand elle invoquée, la fonction setInterval() créé un intervale actif, ce qui signifie qu'une fonction donnée en paramètre est exécutée toutes les x millisecondes — x est la valeur du 2ème paramètre. Ainsi, nous exécutons ici la fonction windBackward() toutes les 200 millisecondes — nous utiliserons cette fonction pour retourner la fonction en arrière de manière constante. Pour stopper un intervale actif, vous devez appeler {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} en lui donnant l'intervale à arrêter en paramètre, dans notre cas il est stocké dans la variable intervalRwd (voir l'appel à clearInterval() effectué plus tôt dans la fonction).

      -
    8. -
    -
  4. -
  5. -

    Pour en finir avec cette section, nous devons définir les fonctions windBackward() et windForward() invoquées dans les appels setInterval(). Ajoutez ce qui suit après les deux fonctions précédentes:

    - -
    function windBackward() {
    -  if(media.currentTime <= 3) {
    -    rwd.classList.remove('active');
    -    clearInterval(intervalRwd);
    -    stopMedia();
    -  } else {
    -    media.currentTime -= 3;
    -  }
    -}
    -
    -function windForward() {
    -  if(media.currentTime >= media.duration - 3) {
    -    fwd.classList.remove('active');
    -    clearInterval(intervalFwd);
    -    stopMedia();
    -  } else {
    -    media.currentTime += 3;
    -  }
    -}
    - Encore une fois, nous allons voir pas à pas la première fonction, puisque les deux fonctions font la même chose mais dans le sens inverse. Dans windBackward(), nous faisons comme suit — gardez à l'esprit que la fonction est exécutée toutes les 200 millisecondes. - -
      -
    1. Nous commençons avec une instruction if qui vérifie si le temps en cours est inférieur à 3 secondes, c'est à dire si le retour arrière nous ramènerait avant le début de la vidéo. Cela provoquerait un comportement étrange. Ainsi, si c'est le cas, nous arrêtons la vidéo en appelant stopMedia(), supprimons la classe active du bouton, et stoppons l'intervale intervalRwd pour stopper le retour arrière. Si nous n'avions pas ajouté cette dernière étape, la vidéo continuerait de se remboniner éternellement.
    2. -
    3. Si le temps en cours n'est pas inférieur à 3 secondes, nous retournons en arrière de 3 secondes en exécutant media.currentTime -= 3. Dans les faits, on rembobine donc la vidéo de 3 secondes toutes les 200 millisecondes.
    4. -
    -
  6. -
- -

Mettre à jour le temps écoulé

- -

La dernière chose à implémenter pour notre lecteur multimédia est l'affichage du temps écoulé. Pour ce faire, nous allons exécuter une fonction pour mettre à jour le temps affiché à chaque fois que l'événement {{event("timeupdate")}} est déclenché sur l'élément <video>. La fréquence à laquelle cet événement se déclenche dépend de votre navigateur, de la puissance de votre CPU, etc (voir post stackoverflow).

- -

Ajoutez la ligne addEventListener() suivante à la suite des autres:

- -
media.addEventListener('timeupdate', setTime);
- -

Maintenant, ajoutez la fonction setTime():

- -
function setTime() {
-  var minutes = Math.floor(media.currentTime / 60);
-  var seconds = Math.floor(media.currentTime - minutes * 60);
-  var minuteValue;
-  var secondValue;
-
-  if (minutes < 10) {
-    minuteValue = '0' + minutes;
-  } else {
-    minuteValue = minutes;
-  }
-
-  if (seconds < 10) {
-    secondValue = '0' + seconds;
-  } else {
-    secondValue = seconds;
-  }
-
-  var mediaTime = minuteValue + ':' + secondValue;
-  timer.textContent = mediaTime;
-
-  var barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
-  timerBar.style.width = barLength + 'px';
-}
-
- -

C'est une fonction assez longue, alors allons-y étape par étape:

- -
    -
  1. Tout d'abord, nous récupérons le nombre de minutes et de secondes à partir de {{domxref("HTMLMediaElement.currentTime")}}.
  2. -
  3. Ensuite, on initialise deux variables supplémentaires — minuteValue et secondValue.
  4. -
  5. Les deux instructions if qui suivent déterminent si le nombre de minutes et secondes est inférieur à 10. Si c'est le cas, on ajoute un zéro à gauche pour afficher le numéro sur deux chiffres — comme sur une horloge digitale.
  6. -
  7. Le temps est au final la concaténation de minuteValue, un caractère deux-points, et secondValue.
  8. -
  9. Le temps qu'on vient de définir devient la valeur {{domxref("Node.textContent")}} du décompte, pour qu'il s'affiche dans l'interface utilisateur.
  10. -
  11. La largeur que nous devons donner <div> intérieur est calculée en récupérant la largeur du <div> externe (la propriété {{domxref("HTMLElement.clientWidth", "clientWidth")}} retourne la largeur de l'élément), et en la multipliant par {{domxref("HTMLMediaElement.currentTime")}} divisé par le total {{domxref("HTMLMediaElement.duration")}} du média.
  12. -
  13. Nous assignons la largeur du <div> intérieur à la valeur calculée, plus "px", il sera donc fixé à ce nombre de pixels.
  14. -
- -

Corriger play et pause

- -

Il nous reste un problème à régler. Si on presse les boutons play/pause ou stop pendant que le retour arrière ou l'avance rapide sont actifs, alors ça ne marchera pas. Comment corriger le code pour qu'ils annulent l'action rwd/fwd et joue/stoppe la vidéo comme on s'y attendrait? C'est relativement simple.

- -

Tout d'abord, ajoutez les lignes qui suivent à l'intérieur de la fonction stopMedia() — n'importe où:

- -
rwd.classList.remove('active');
-fwd.classList.remove('active');
-clearInterval(intervalRwd);
-clearInterval(intervalFwd);
-
- -

Maintenant, ajoutez ces mêmes lignes une fois de plus, au début de la fonction playPauseMedia() (juste avant le début de l'instruction if).

- -

À ce stade, vous pouvez supprimer les lignes équivalentes des fonctions windBackward() et windForward(), puisqu'elles ont été ajoutées à la fonction stopMedia() à la place.

- -
-

Note : Vous pouvez améliorer votre code en créant une fonction séparée qui exécute ces lignes, et l'appeler aux endroits où vous en avez besoin plutôt que de répéter ces lignes à de multiples endroits du code. Mais nous vous laissons vous en occuper.

-
- -
-

Note : Le code terminé est disponible sur Github (le voir en direct).

-
- -

Sommaire

- -

Je pense que nous vous en avons suffisamment appris dans cet article. L'API {{domxref("HTMLMediaElement")}} offre une multitude de fonctionnalités pour la création de lecteurs audio et vidéo simples, et ce n'est que le sommet de l'iceberg. La section "Voir aussi" ci-dessous vous fournirea des liens vers des fonctionnalités plus complexes et plus intéressantes.

- -

Voici quelques suggestions de modifications à apporter à l'exemple que nous avons construit:

- -
    -
  1. -

    Si la vidéo dure plus d'une heure, le temps écoulé va bien afficher les minutes et les secondes mais pas les heures. Changez l'exemple pour lui faire afficher les heures.

    -
  2. -
  3. -

    Parce que les éléments <audio> ont la même fonctionnalité {{domxref("HTMLMediaElement")}} de disponible, vous pouvez faire fonctionner ce lecteur avec un élément <audio>. Essayez  de le faire.

    -
  4. -
  5. -

    Trouvez un moyen de transformer le <div> interne en une véritable barre de progrès — quand vous cliquez quelque part sur la barre, vous vous déplacez à la position relative dans la vidéo. Un indice: vous pouvez trouver les valeurs X et Y des côtés gauche/droite et haut/bas d'un l'élément via la méthode getBoundingClientRect(), et vous pouvez trouver les coordonnées de la souris au moment du clic à l'intérieur de l'objet event du clic, appelé sur l'objet {{domxref("Document")}}. Par exemple:

    - -
    document.onclick = function(e) {
    -  console.log(e.x) + ',' + console.log(e.y)
    -}
    -
  6. -
- -

Voir aussi

- - - -

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

- -

 

- -

Dans ce module

- - diff --git a/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.md b/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.md new file mode 100644 index 0000000000..a091835f18 --- /dev/null +++ b/files/fr/learn/javascript/client-side_web_apis/video_and_audio_apis/index.md @@ -0,0 +1,519 @@ +--- +title: APIs vidéo et audio +slug: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs +tags: + - API + - Apprendre + - Article + - Audio + - Codage + - Débutant + - Guide + - JavaScript + - Video +translation_of: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs +original_slug: Apprendre/JavaScript/Client-side_web_APIs/Video_and_audio_APIs +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}
+ +

HTML5 fournit des éléments pour intégrer du multimédia dans les documents — {{htmlelement("video")}} et {{htmlelement("audio")}} — et qui viennent avec leurs propres APIs pour contrôler la lecture, se déplacer dans le flux, etcCet article montre comment réaliser les tâches les plus communes, comme créer des contrôles de lectures personnalisés.

+ + + + + + + + + + + + +
Prérequis:Les bases du JavaScript (voir premiers pas en JavaScript, les briques Javascript, Introduction aux objets), Introduction aux APIs web
Objectif:Apprendre à utiliser les APIs du navigateur pour contrôler la lecture de audio et vidéo.
+ +

Les balises HTML5 video et audio

+ +

Les balises {{htmlelement("video")}} et {{htmlelement("audio")}} permettent d'intégrer des vidéos et de l'audio dans des pages web. Comme nous l'avons montré dans Contenu audio et vidéo, une implémentation habituelle ressemble à ça :

+ +
<video controls>
+  <source src="rabbit320.mp4" type="video/mp4">
+  <source src="rabbit320.webm" type="video/webm">
+  <p>Votre navigateur ne supporte pas la vidéo HTML5. Voici à la place <a href="rabbit320.mp4">un lien vers la vidéo</a>.</p>
+</video>
+ +

Cela crée un lecteur vidéo à l'intérieur du navigateur:

+ +

{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html", '100%', 380)}}

+ +

Vous pouvez consulter toutes fonctionnalités HTML audio et vidéo dans l'article mentionné précédemment. Pour notre utilisation ici, l'attribut le plus intéressant est {{htmlattrxref("controls", "video")}}. Il permet d'activer l'ensemble des contrôles de lecture par défaut; si vous ne le spécifiez pas, vous aucun contrôle ne sera affiché:

+ +

{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html", '100%', 380)}}

+ +

Ce n'est pas immédiatement utile pour la lecture de vidéos, mais ça a des avantages. Les contrôles natifs des navigateurs différent complètement d'un navigateur à l'autre — ce qui est embêtant pour un support global des différents navigateurs. Un autre problème est que le contrôles natifs sont généralement assez peu accessibles au clavier.

+ +

Vous pouvez régler ces deux problèmes en cachant les contrôles natifs (en supprimant l'attribut controls) et en les remplaçant par les votres en HTML, CSS et JavaScript. Dans la prochaine section, nous verrons les outils de base à notre disposition pour faire ça.

+ +

L'API HTMLMediaElement

+ +

L'API {{domxref("HTMLMediaElement")}}, spécifiée dans HTML5, fournit des fonctionnalités qui permettent de controller des lecteurs audio et vidéo avec JavaScript — avec par exemple {{domxref("HTMLMediaElement.play()")}} ou encore {{domxref("HTMLMediaElement.pause()")}}. Cette interface est disponible à la fois pour les balises {{htmlelement("audio")}} et {{htmlelement("video")}}, les fonctionnalités utiles pour les deux étant quasiment identiques. Voyons un exemple pour découvrir ces fonctionnalités.

+ +

Notre exemple final ressemblera (et fonctionnera) comme ceci:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/video-audio/finished/", '100%', 360)}}

+ +

Débuter

+ +

Pour commencer avec cet exemple, télechargez notre media-player-start.zip et décompressez-le dans un nouveau dossier sur votre disque dur. Si vous avez téléchargé notre dépôt d'exemples, vous le trouverez dans javascript/apis/video-audio/start/.

+ +

Si vous ouvrez la page HTML, vous devriez voir un lecteur HTML5 utilisant les contrôles natifs.

+ +

Exploration du HTML

+ +

Ouvrez le fichier HTML d'index. Vous allez voir que le HTML contient majoritairement du code pour le lecteur et ses contrôles:

+ +
<div class="player">
+  <video controls>
+    <source src="video/sintel-short.mp4" type="video/mp4">
+    <source src="video/sintel-short.mp4" type="video/webm">
+    <!-- fallback contenu ici -->
+  </video>
+  <div class="controls">
+    <button class="play" data-icon="P" aria-label="bascule lecture pause"></button>
+    <button class="stop" data-icon="S" aria-label="stop"></button>
+    <div class="timer">
+      <div></div>
+      <span aria-label="timer">00:00</span>
+    </div>
+    <button class="rwd" data-icon="B" aria-label="retour arrière"></button>
+    <button class="fwd" data-icon="F" aria-label="avance rapide"></button>
+  </div>
+</div>
+
+ + + +

Exploration du CSS

+ +

Maintenant, ouvrez le fichier CSS et jetez-y un coup d'oeil. Le CSS pour cet exemple n'est pas très compliqué, mais nous allons voir les éléments les plus intéressants ici. Tout d'abord, le style de .controls:

+ +
.controls {
+  visibility: hidden;
+  opacity: 0.5;
+  width: 400px;
+  border-radius: 10px;
+  position: absolute;
+  bottom: 20px;
+  left: 50%;
+  margin-left: -200px;
+  background-color: black;
+  box-shadow: 3px 3px 5px black;
+  transition: 1s all;
+  display: flex;
+}
+
+.player:hover .controls, player:focus .controls {
+  opacity: 1;
+}
+
+ + + +

Ensuite, voyons les icônes des boutons:

+ +
@font-face {
+   font-family: 'HeydingsControlsRegular';
+   src: url('fonts/heydings_controls-webfont.eot');
+   src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
+        url('fonts/heydings_controls-webfont.woff') format('woff'),
+        url('fonts/heydings_controls-webfont.ttf') format('truetype');
+   font-weight: normal;
+   font-style: normal;
+}
+
+button:before {
+  font-family: HeydingsControlsRegular;
+  font-size: 20px;
+  position: relative;
+  content: attr(data-icon);
+  color: #aaa;
+  text-shadow: 1px 1px 0px black;
+}
+ +

Tout d'abord, en haut du CSS, nous utilisons un bloc {{cssxref("@font-face")}} pour importer une police web personnalisée. Il s'agit d'une police d'icônes — tous les caractères de l'alphabet correspondent à des icônes que vous pouvez utiliser dans votre application.

+ +

Ensuite, nous générons du contenu pour afficher une icône sur chaque bouton:

+ + + +

Les polices d'icônes sont très cool pour de nombreuses raisons: réduire les requêtes HTTP (puisque vous n'avez pas besoin de télécharger des icônes sous forme de fichiers image), bonne scalabilité, et le fait que vous pouvez utiliser les propriétés de texte pour les formatter — comme {{cssxref("color")}} et {{cssxref("text-shadow")}}.

+ +

Dernier point mais non des moindres, le CSS du décompte:

+ +
.timer {
+  line-height: 38px;
+  font-size: 10px;
+  font-family: monospace;
+  text-shadow: 1px 1px 0px black;
+  color: white;
+  flex: 5;
+  position: relative;
+}
+
+.timer div {
+  position: absolute;
+  background-color: rgba(255,255,255,0.2);
+  left: 0;
+  top: 0;
+  width: 0;
+  height: 38px;
+  z-index: 2;
+}
+
+.timer span {
+  position: absolute;
+  z-index: 3;
+  left: 19px;
+}
+ + + +

Implémenter le JavaScript

+ +

Nous avons déjà une interface HTML et CSS assez complète; nous avons maintenant besoin de gérer les boutons pour que les contrôles fonctionnent.

+ +
    +
  1. +

    Créez un nouveau fichier JavaScript dans le même répertoire que votre fichier index.html. Nous l'appelerons custom-player.js.

    +
  2. +
  3. +

    En haut de ce fichier, insérez le code suivant:

    + +
    var media = document.querySelector('video');
    +var controls = document.querySelector('.controls');
    +
    +var play = document.querySelector('.play');
    +var stop = document.querySelector('.stop');
    +var rwd = document.querySelector('.rwd');
    +var fwd = document.querySelector('.fwd');
    +
    +var timerWrapper = document.querySelector('.timer');
    +var timer = document.querySelector('.timer span');
    +var timerBar = document.querySelector('.timer div');
    +
    + +

    Ici, nous créons des variables pour stocker les références de tous les objets que nous voulons manipuler. Nous avons trois groupes:

    + +
      +
    • L'élément <video>, et la barre de contrôle.
    • +
    • Les boutons play/pause, stop, retour arrière, et avance rapide.
    • +
    • Le <div> externe, le <span> qui décompte le temps écoulé, et le <div> interne qui affiche le progrès de la vidéo.
    • +
    +
  4. +
  5. +

    Ensuite, insérez ce qui suit en bas de votre code:

    + +
    media.removeAttribute('controls');
    +controls.style.visibility = 'visible';
    + +

    Ces deux lignes suppriment les contrôles par défaut du navigateur sur la vidéo, et rendent nos contrôles personnalisés visibles.

    +
  6. +
+ +

Lecture et pause de la vidéo

+ +

Imlémentons le contrôle le plus important — le bouton play/pause.

+ +
    +
  1. +

    Tout d'abord, ajoutez ce qui suit au bas de votre code, pour que la fonction playPauseMedia() soit invoquée quand le bouton play est cliqué:

    + +
    play.addEventListener('click', playPauseMedia);
    +
    +
  2. +
  3. +

    Maintenant, définissons playPauseMedia() — ajoutez ce qui suit, toujours au bas de votre code:

    + +
    function playPauseMedia() {
    +  if(media.paused) {
    +    play.setAttribute('data-icon','u');
    +    media.play();
    +  } else {
    +    play.setAttribute('data-icon','P');
    +    media.pause();
    +  }
    +}
    + +

    Ici, nous utilisons une instruction if pour vérifier si la vidéo est en pause. La propriété {{domxref("HTMLMediaElement.paused")}} retourne vrai si le média est en pause — c'est le cas quand la vidéo n'est pas en cours de lecture, y compris quand la vidéo est au début après son chargement. Si elle est en pause, nous définissons la valeur de l'attribut data-icon à "u", qui est une icône "en pause", et invoquons la  méthode {{domxref("HTMLMediaElement.play()")}} pour jouer le média.

    + +

    Au second clic, le bouton sera de nouveau alterné — l'icône "play" sera affiché, et la vidéo sera mise en pause avec {{domxref("HTMLMediaElement.paused()")}}.

    +
  4. +
+ +

Stopper la vidéo

+ +
    +
  1. +

    Ajoutons la possibilité d'arrêter la vidéo. Ajoutez les lignes addEventListener() suivantes au-dessous de vos ajouts précédents:

    + +
    stop.addEventListener('click', stopMedia);
    +media.addEventListener('ended', stopMedia);
    +
    + +

    L'événement {{event("click")}} est explicite — nous voulons stopper la vidéo en appelant la fonction stopMedia() quand le bouton stop est cliqué. Cependant, nous voulons également stopper la vidéo quand elle a fini de jouer — signalé par l'événement {{event("ended")}}, nous pouvons donc mettre en place un gestionnaire d'événement pour exécuter la fonction quand cet événément se produit.

    +
  2. +
  3. +

    Ensuite, définissons stopMedia() — ajoutez ce qui suit après la fonction playPauseMedia():

    + +
    function stopMedia() {
    +  media.pause();
    +  media.currentTime = 0;
    +  play.setAttribute('data-icon','P');
    +}
    +
    + +

    Il n'y a pas de méthode stop() dans l'API  HTMLMediaElement — l'équivalent du stop est de mettre pause() sur la vidéo, et de définir la propriété {{domxref("HTMLMediaElement.currentTime","currentTime")}} à 0. Définir une valeur à currentTime (en secondes) change immédiatement la position du temps du média.

    + +

    Tout ce qui nous reste à faire après ça est d'afficher l'icône "play". Que la vidéo ait été en train de jouer ou en pause, quand le bouton stop est pressé, vous voulez qu'elle doit prête à être lue.

    +
  4. +
+ +

Retour arrière et avance rapide

+ +

Il y a différentes manières d'implémenter le retour arrière et l'avance rapide; ici, nous vous montrons une manière relativement complexe de le faire, qui n'a pas de comportement inattendu quand différents boutons sont pressés dans un ordre aléatoire.

+ +
    +
  1. +

    Tout d'abord, ajoutez les lignes addEventListener() suivantes à la suite des précédentes:

    + +
    rwd.addEventListener('click', mediaBackward);
    +fwd.addEventListener('click', mediaForward);
    +
    +
  2. +
  3. +

    Maintenant, occupons-nous des fonctions des gestionnaires d'événément — ajoutez le code suivant à la suite des fonctions précédentes pour définir mediaBackward() et mediaForward():

    + +
    var intervalFwd;
    +var intervalRwd;
    +
    +function mediaBackward() {
    +  clearInterval(intervalFwd);
    +  fwd.classList.remove('active');
    +
    +  if(rwd.classList.contains('active')) {
    +    rwd.classList.remove('active');
    +    clearInterval(intervalRwd);
    +    media.play();
    +  } else {
    +    rwd.classList.add('active');
    +    media.pause();
    +    intervalRwd = setInterval(windBackward, 200);
    +  }
    +}
    +
    +function mediaForward() {
    +  clearInterval(intervalRwd);
    +  rwd.classList.remove('active');
    +
    +  if(fwd.classList.contains('active')) {
    +    fwd.classList.remove('active');
    +    clearInterval(intervalFwd);
    +    media.play();
    +  } else {
    +    fwd.classList.add('active');
    +    media.pause();
    +    intervalFwd = setInterval(windForward, 200);
    +  }
    +}
    +
    + +

    Vous remarquerez que nous commençons par initialiser deux variables — intervalFwd et intervalRwd — vous verrez à quoi elles servent plus tard.

    + +

    Voyons pas à pas mediaBackward() (mediaForward() fait la même chose, mais dans l'autre sens):

    + +
      +
    1. Nous effaçons les classes et intervales qui sont définits sur la fonctionnalité d'avance rapide — de cette manière, si on presse le bouton rwd après avoir pressé le bouton fwd, on annule l'avance rapide et la remplaçons avec le retour arrière. Si on essayait de faire les deux à la fois, le lecteur échouerait.
    2. +
    3. Nous utilisons une instruction if pour vérifier si la classe active a été définie sur le bouton rwd, ce qui indique qu'il a déjà été pressé. La propriété {{domxref("classList")}} est une propriété plutôt pratique qui existe sur chaque élément — elle contient une liste de toutes les classes définies sur l'élément, ainsi que des méthodes pour en ajouter/supprimer, etc. Nous utilisons la méthode classList.contains() pour vérifier si la liste contient la classe active. Cela retourne un booléen true/false en résultat.
    4. +
    5. Si la classe active a été définie sur le bouton rwd, nous la supprimons avec classList.remove(), effaçons l'intervale qui a été définit sur le bouton quand il a été pressé (voir ci-dessous pour plus d'explication), et utilisons {{domxref("HTMLMediaElement.play()")}} pour annuler le retour arrière et démarrer la vidéo normalement.
    6. +
    7. +

      Sinon, nous ajoutons la classe active sur le bouton rwd avec classList.add(), mettons la vidéo en pause en utilisant {{domxref("HTMLMediaElement.pause()")}}, puis définissons la variable intervalRwd en appelant {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}. Quand elle invoquée, la fonction setInterval() créé un intervale actif, ce qui signifie qu'une fonction donnée en paramètre est exécutée toutes les x millisecondes — x est la valeur du 2ème paramètre. Ainsi, nous exécutons ici la fonction windBackward() toutes les 200 millisecondes — nous utiliserons cette fonction pour retourner la fonction en arrière de manière constante. Pour stopper un intervale actif, vous devez appeler {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} en lui donnant l'intervale à arrêter en paramètre, dans notre cas il est stocké dans la variable intervalRwd (voir l'appel à clearInterval() effectué plus tôt dans la fonction).

      +
    8. +
    +
  4. +
  5. +

    Pour en finir avec cette section, nous devons définir les fonctions windBackward() et windForward() invoquées dans les appels setInterval(). Ajoutez ce qui suit après les deux fonctions précédentes:

    + +
    function windBackward() {
    +  if(media.currentTime <= 3) {
    +    rwd.classList.remove('active');
    +    clearInterval(intervalRwd);
    +    stopMedia();
    +  } else {
    +    media.currentTime -= 3;
    +  }
    +}
    +
    +function windForward() {
    +  if(media.currentTime >= media.duration - 3) {
    +    fwd.classList.remove('active');
    +    clearInterval(intervalFwd);
    +    stopMedia();
    +  } else {
    +    media.currentTime += 3;
    +  }
    +}
    + Encore une fois, nous allons voir pas à pas la première fonction, puisque les deux fonctions font la même chose mais dans le sens inverse. Dans windBackward(), nous faisons comme suit — gardez à l'esprit que la fonction est exécutée toutes les 200 millisecondes. + +
      +
    1. Nous commençons avec une instruction if qui vérifie si le temps en cours est inférieur à 3 secondes, c'est à dire si le retour arrière nous ramènerait avant le début de la vidéo. Cela provoquerait un comportement étrange. Ainsi, si c'est le cas, nous arrêtons la vidéo en appelant stopMedia(), supprimons la classe active du bouton, et stoppons l'intervale intervalRwd pour stopper le retour arrière. Si nous n'avions pas ajouté cette dernière étape, la vidéo continuerait de se remboniner éternellement.
    2. +
    3. Si le temps en cours n'est pas inférieur à 3 secondes, nous retournons en arrière de 3 secondes en exécutant media.currentTime -= 3. Dans les faits, on rembobine donc la vidéo de 3 secondes toutes les 200 millisecondes.
    4. +
    +
  6. +
+ +

Mettre à jour le temps écoulé

+ +

La dernière chose à implémenter pour notre lecteur multimédia est l'affichage du temps écoulé. Pour ce faire, nous allons exécuter une fonction pour mettre à jour le temps affiché à chaque fois que l'événement {{event("timeupdate")}} est déclenché sur l'élément <video>. La fréquence à laquelle cet événement se déclenche dépend de votre navigateur, de la puissance de votre CPU, etc (voir post stackoverflow).

+ +

Ajoutez la ligne addEventListener() suivante à la suite des autres:

+ +
media.addEventListener('timeupdate', setTime);
+ +

Maintenant, ajoutez la fonction setTime():

+ +
function setTime() {
+  var minutes = Math.floor(media.currentTime / 60);
+  var seconds = Math.floor(media.currentTime - minutes * 60);
+  var minuteValue;
+  var secondValue;
+
+  if (minutes < 10) {
+    minuteValue = '0' + minutes;
+  } else {
+    minuteValue = minutes;
+  }
+
+  if (seconds < 10) {
+    secondValue = '0' + seconds;
+  } else {
+    secondValue = seconds;
+  }
+
+  var mediaTime = minuteValue + ':' + secondValue;
+  timer.textContent = mediaTime;
+
+  var barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
+  timerBar.style.width = barLength + 'px';
+}
+
+ +

C'est une fonction assez longue, alors allons-y étape par étape:

+ +
    +
  1. Tout d'abord, nous récupérons le nombre de minutes et de secondes à partir de {{domxref("HTMLMediaElement.currentTime")}}.
  2. +
  3. Ensuite, on initialise deux variables supplémentaires — minuteValue et secondValue.
  4. +
  5. Les deux instructions if qui suivent déterminent si le nombre de minutes et secondes est inférieur à 10. Si c'est le cas, on ajoute un zéro à gauche pour afficher le numéro sur deux chiffres — comme sur une horloge digitale.
  6. +
  7. Le temps est au final la concaténation de minuteValue, un caractère deux-points, et secondValue.
  8. +
  9. Le temps qu'on vient de définir devient la valeur {{domxref("Node.textContent")}} du décompte, pour qu'il s'affiche dans l'interface utilisateur.
  10. +
  11. La largeur que nous devons donner <div> intérieur est calculée en récupérant la largeur du <div> externe (la propriété {{domxref("HTMLElement.clientWidth", "clientWidth")}} retourne la largeur de l'élément), et en la multipliant par {{domxref("HTMLMediaElement.currentTime")}} divisé par le total {{domxref("HTMLMediaElement.duration")}} du média.
  12. +
  13. Nous assignons la largeur du <div> intérieur à la valeur calculée, plus "px", il sera donc fixé à ce nombre de pixels.
  14. +
+ +

Corriger play et pause

+ +

Il nous reste un problème à régler. Si on presse les boutons play/pause ou stop pendant que le retour arrière ou l'avance rapide sont actifs, alors ça ne marchera pas. Comment corriger le code pour qu'ils annulent l'action rwd/fwd et joue/stoppe la vidéo comme on s'y attendrait? C'est relativement simple.

+ +

Tout d'abord, ajoutez les lignes qui suivent à l'intérieur de la fonction stopMedia() — n'importe où:

+ +
rwd.classList.remove('active');
+fwd.classList.remove('active');
+clearInterval(intervalRwd);
+clearInterval(intervalFwd);
+
+ +

Maintenant, ajoutez ces mêmes lignes une fois de plus, au début de la fonction playPauseMedia() (juste avant le début de l'instruction if).

+ +

À ce stade, vous pouvez supprimer les lignes équivalentes des fonctions windBackward() et windForward(), puisqu'elles ont été ajoutées à la fonction stopMedia() à la place.

+ +
+

Note : Vous pouvez améliorer votre code en créant une fonction séparée qui exécute ces lignes, et l'appeler aux endroits où vous en avez besoin plutôt que de répéter ces lignes à de multiples endroits du code. Mais nous vous laissons vous en occuper.

+
+ +
+

Note : Le code terminé est disponible sur Github (le voir en direct).

+
+ +

Sommaire

+ +

Je pense que nous vous en avons suffisamment appris dans cet article. L'API {{domxref("HTMLMediaElement")}} offre une multitude de fonctionnalités pour la création de lecteurs audio et vidéo simples, et ce n'est que le sommet de l'iceberg. La section "Voir aussi" ci-dessous vous fournirea des liens vers des fonctionnalités plus complexes et plus intéressantes.

+ +

Voici quelques suggestions de modifications à apporter à l'exemple que nous avons construit:

+ +
    +
  1. +

    Si la vidéo dure plus d'une heure, le temps écoulé va bien afficher les minutes et les secondes mais pas les heures. Changez l'exemple pour lui faire afficher les heures.

    +
  2. +
  3. +

    Parce que les éléments <audio> ont la même fonctionnalité {{domxref("HTMLMediaElement")}} de disponible, vous pouvez faire fonctionner ce lecteur avec un élément <audio>. Essayez  de le faire.

    +
  4. +
  5. +

    Trouvez un moyen de transformer le <div> interne en une véritable barre de progrès — quand vous cliquez quelque part sur la barre, vous vous déplacez à la position relative dans la vidéo. Un indice: vous pouvez trouver les valeurs X et Y des côtés gauche/droite et haut/bas d'un l'élément via la méthode getBoundingClientRect(), et vous pouvez trouver les coordonnées de la souris au moment du clic à l'intérieur de l'objet event du clic, appelé sur l'objet {{domxref("Document")}}. Par exemple:

    + +
    document.onclick = function(e) {
    +  console.log(e.x) + ',' + console.log(e.y)
    +}
    +
  6. +
+ +

Voir aussi

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}

+ +

 

+ +

Dans ce module

+ + -- cgit v1.2.3-54-g00ecf