LIFWEB TP2-b - Manipulation du DOM

Pour cet exercice, on fournit de nouveaux fichiers de départ :

Exercice 0 (PRÉPARATION) : compréhension

  • Supprimer les variables globales utilisées dans les exercices 4 et 5 du TP2-a en limitant leur portée grâce à une IIFE.
  • Dans le code fourni pour ce TP2-b, justifier l’intérêt de la ligne $imageHref.dispatchEvent(new Event("input"));.
  • Toujours dans le même code, explique la ligne !$imageHref.checkValidity() || $imageHref.value === "".

Exercice 1 : Gestion de galerie d’images

Cet exercice permet de faire un point d’étape : mesurer le temps mis pour finir cet exercice ou à défaut, le point d’arrivée à la fin du temps consacré. Il est estimé qu’un enseignant mette environ 45 minute et le double pour un⋅e étudiant⋅e qui a fait tous les exercices des TPs précédents (bonus inclus). Les deux premiers bonus de ce sujet font découvrir de nouvelles APIs et doivent rendre le code final plus simple. Le denier bonus est une alternative proposée par un⋅e des étudiant⋅e⋅s.

On s’inspire du sujet d’Arnaud Couturier à l’UNC qui consiste à gérer une galerie d’images, comme illustré dans sa vidéo. Les fonctionnalités sont comme suit :

  • On ajoute une image dans la page grâce au bouton d’ajout en lisant l’URL saisie (le projet de départ rempli automatiquement le champ quand on clique sur une des adresses d’exemple).
  • Chaque image ajoutée peut être supprimée, déplacée avant ou déplacée après avec les icônes ✖, ▲, et ▼.
  • Le bouton ▲ (respectivement, ▼) est désactivé si l’image est déjà au début (respectivement, à la fin) de la liste.

Il faudra penser au problème de mise-à-jour quand une image devient première ou dernière après un déplacement ou une suppression. Une solution qui, lors d’une modification de la liste, parcours toutes les images présentes pour activer/désactiver les boutons est acceptable. Les méthodes suivantes du DOM seront utiles :

  • Element.firstElementChild (MDN) ;
  • Element.lastElementChild (MDN) ;
  • Element.previousElementSibling (MDN) ;
  • Element.nextElementSibling (MDN) ;
  • Element.before (MDN) ;
  • Element.after (MDN).

On prendra soin de respecter les bonnes pratiques du CM2 et d’éviter les variables globales.

Exercice 2 (BONUS) : API MutationObserver

Il est possible de scruter les modifications d’un Node (ou d’un Element) du DOM avec la classe MutationObserver (MDN, javascript.info).

const observer = new MutationObserver((mutationRecords) => {
  // traiter les modifications
  console.debug(mutationRecords);
});

// on observe les changements sur les fils du conteneur
observer.observe($imageContainer, { childList: true, subtree: false });

Ceci permet de centraliser la désactivation des boutons de la première et dernière image : on évite ainsi de dupliquer du code et l’appel systématique à la vérification après mise-à-jour.

Exercice 3 (BONUS) : Élement <template>

Pour regrouper l’image et ses boutons, on créera certainement un <div> avec quatre enfants. Plutôt que de créer ces éléments directement avec l’API DOM, on peut transformer ce conteneur en <template> que l’on ajoutera au HTML. Pour chaque nouvelle image, on clonera alors ce modèle comme suit :

const template = document.querySelector("#image-template");
const $imageRoot = template.content.cloneNode(true);
/* traitement sur les enfants d'$imageRoot */
$imageContainer.append($imageRoot);

⚠️ Dans l’exemple $imageRootest un HTMLTemplateElement dont l’attribut HTMLTemplateElement.content (MDN) est un DocumentFragment (MDN) dont seuls les enfants sont ajoutés au DOM.

Exercice 4 (BONUS) : Alternative CSS

Une méthode alternative, à la spécification différente, pour la gestion des boutons consiste à les cacher au lieu de les désactiver. Ceci peut se faire en CSS pur, ainsi, il n’y aura plus besoin de parcourir les boutons en JS pour déterminer ceux qui sont actifs. On utilisera les pseudo-classes :first-child et :last-child qui permettent de sélectionner le premier et dernier fils avec display: none pour cacher l’élément.

Par exemple document.querySelector("#image-container div:last-child") sélectionne le dernier <div> de l’élément #image-container.