API HTML5 Glisser-déposer

Cet article explique les bases du glisser-déposer.

Créer du contenu déplaçable

Dans la plupart des navigateurs, les sélections de texte, les images et les liens sont déplaçables par défaut. Par exemple, si vous faites glisser un lien sur une page Web, une petite zone contenant un titre et une URL s'affiche. Vous pouvez les déposer dans la barre d'adresse ou sur le bureau pour créer un raccourci ou accéder au lien. Pour rendre d'autres types de contenu déplaçables, vous devez utiliser les API de glisser-déposer HTML5.

Pour rendre un objet déplaçable, définissez draggable=true sur cet élément. Il est possible de faire glisser presque tout, y compris les images, les fichiers, les liens, les fichiers ou tout autre balisage sur votre page.

L'exemple suivant crée une interface permettant de réorganiser les colonnes qui ont été mises en page avec CSS Grid. Le balisage de base des colonnes se présente comme suit, avec l'attribut draggable de chaque colonne défini sur true :

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

Voici le code CSS des éléments "container" et "box". Le seul CSS associé à la fonctionnalité de déplacement est la propriété cursor: move. Le reste du code contrôle la mise en page et le style des éléments "container" et "box".

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

À ce stade, vous pouvez faire glisser les éléments, mais rien d'autre ne se passe. Pour ajouter un comportement, vous devez utiliser l&#39;API JavaScript.

Écouter les événements de déplacement

Pour surveiller le processus de déplacement, vous pouvez écouter l'un des événements suivants:

Pour gérer le flux de déplacement, vous avez besoin d'un type d'élément source (où commence le déplacement), de la charge utile de données (l'élément déplacé) et d'une cible (une zone pour attraper le déplacement). L'élément source peut correspondre à presque n'importe quel type d'élément. La cible est la zone de dépôt ou l'ensemble de zones de dépôt qui accepte les données que l'utilisateur tente de supprimer. Tous les éléments ne peuvent pas être des cibles. Par exemple, votre cible ne peut pas être une image.

Commencer et terminer une séquence de déplacement

Après avoir défini les attributs draggable="true" sur votre contenu, associez un gestionnaire d'événements dragstart pour lancer la séquence de déplacement pour chaque colonne.

Ce code définit l&#39;opacité de la colonne sur 40% lorsque l&#39;utilisateur commence à la faire glisser, puis la rétablit à 100% à la fin de l&#39;événement de glissement.

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

Vous pouvez voir le résultat dans la démonstration Glitch suivante. Lorsque vous faites glisser un élément, son opacité change. Étant donné que l'élément source contient l'événement dragstart, définir this.style.opacity sur 40 % fournit à l'utilisateur un indice visuel indiquant que cet élément est la sélection en cours de déplacement. Lorsque vous déposez l'élément, l'opacité de l'élément source retrouve 100 %, même si vous n'avez pas encore défini le comportement de dépôt.

Ajouter des repères visuels supplémentaires

Pour aider l'utilisateur à comprendre comment interagir avec votre interface, utilisez les gestionnaires d'événements dragenter, dragover et dragleave. Dans cet exemple, les colonnes sont des cibles de dépôt en plus d'être déplaçables. Aidez l'utilisateur à comprendre cela en rendant la bordure en pointillés lorsqu'il maintient un élément déplacé au-dessus d'une colonne. Par exemple, dans votre CSS, vous pouvez créer une classe over pour les éléments qui sont des cibles de dépôt:

.box.over {
  border: 3px dotted #666;
}

Ensuite, dans votre code JavaScript, configurez les gestionnaires d'événements, ajoutez la classe over lorsque vous faites glisser la colonne et supprimez-la lorsque l'élément déplacé quitte l'élément. Dans le gestionnaire dragend, nous nous assurons également de supprimer les classes à la fin du glisser-déposer.

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

Voici quelques points à prendre en compte dans ce code :

  • L'action par défaut pour l'événement dragover consiste à définir la propriété dataTransfer.dropEffect sur "none". La propriété dropEffect est abordée plus loin sur cette page. Pour l'instant, sachez simplement qu'il empêche le déclenchement de l'événement drop. Pour ignorer ce comportement, appelez e.preventDefault(). Une autre bonne pratique consiste à renvoyer false dans ce même gestionnaire.

  • Le gestionnaire d'événements dragenter permet d'activer/de désactiver la classe over au lieu de dragover. Si vous utilisez dragover, l'événement se déclenche à plusieurs reprises lorsque l'utilisateur maintient l'élément glissé au-dessus d'une colonne, ce qui entraîne l'activation et la désactivation répétées de la classe CSS. Le navigateur effectue alors de nombreuses opérations d'affichage inutiles, ce qui peut affecter l'expérience utilisateur. Nous vous recommandons vivement de réduire le nombre de redessins et, si vous devez utiliser dragover, envisagez de limiter ou de bloquer votre écouteur d'événements.

Effectuer le dépôt

Pour traiter la suppression, ajoutez un écouteur d'événements pour l'événement drop. Dans le gestionnaire drop, vous devez empêcher le comportement par défaut du navigateur concernant les suppressions, qui est généralement une redirection agaçante. Pour ce faire, appelez e.stopPropagation().

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

Veillez à enregistrer le nouveau gestionnaire en même temps que les autres gestionnaires:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

Si vous exécutez le code à ce stade, l'élément ne s'affiche pas à son nouvel emplacement. Pour ce faire, utilisez l'objet DataTransfer.

La propriété dataTransfer contient les données envoyées lors d'une action de glisser-déposer. dataTransfer est défini dans l'événement dragstart et lu ou géré dans l'événement de dépôt. L'appel de e.dataTransfer.setData(mimeType, dataPayload) vous permet de définir le type MIME et la charge utile des données de l'objet.

Dans cet exemple, nous allons permettre aux utilisateurs de réorganiser l'ordre des colonnes. Pour ce faire, vous devez d'abord stocker le code HTML de l'élément source au début du déplacement:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

Dans l'événement drop, vous traitez la suppression de colonne en définissant le code HTML de la colonne source sur le code HTML de la colonne cible dans laquelle vous avez supprimé les données. Cela inclut de vérifier que l&#39;utilisateur ne dépose pas l&#39;élément sur la même colonne à partir de laquelle il l&#39;a fait glisser.

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

Vous pouvez voir le résultat dans la démonstration suivante. Pour ce faire, vous avez besoin d&#39;un navigateur pour ordinateur. L&#39;API Drag and Drop n&#39;est pas compatible avec les appareils mobiles. Faites glisser et relâchez la colonne A au-dessus de la colonne B et notez comment elles se déplacent:

Autres propriétés de glisser-déposer

L'objet dataTransfer expose les propriétés pour fournir un retour visuel à l'utilisateur pendant le processus de déplacement et contrôler la manière dont chaque cible de dépôt répond à un type de données particulier.

  • dataTransfer.effectAllowed restreint le type de "glissement" que l'utilisateur peut effectuer sur l'élément. Elle est utilisée dans le modèle de traitement par glisser-déposer pour initialiser dropEffect lors des événements dragenter et dragover. La propriété peut avoir les valeurs suivantes: none, copy, copyLink, copyMove, link, linkMove, move, all et uninitialized.
  • dataTransfer.dropEffect contrôle les commentaires que l'utilisateur reçoit lors des événements dragenter et dragover. Lorsque l'utilisateur maintient son pointeur sur un élément cible, le curseur du navigateur indique le type d'opération qui va avoir lieu, par exemple une copie ou un déplacement. L'effet peut prendre l'une des valeurs suivantes : none, copy, link ou move.
  • e.dataTransfer.setDragImage(imgElement, x, y) signifie que, au lieu d'utiliser le retour d'image fantôme par défaut du navigateur, vous pouvez définir une icône de glisser-déposer.

Importer des fichiers

Cet exemple simple utilise une colonne à la fois comme source et comme cible de glisser-déposer. Cela peut se produire dans une UI qui demande à l'utilisateur de réorganiser des éléments. Dans certains cas, la cible et la source du déplacement peuvent être des types d'éléments différents, par exemple dans une interface où l'utilisateur doit sélectionner une image comme image principale d'un produit en faisant glisser l'image sélectionnée sur une cible.

La fonctionnalité de glisser-déposer est souvent utilisée pour permettre aux utilisateurs de faire glisser des éléments de leur bureau vers une application. La principale différence réside dans votre gestionnaire drop. Au lieu d'utiliser dataTransfer.getData() pour accéder aux fichiers, leurs données sont contenues dans la propriété dataTransfer.files:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

Pour en savoir plus à ce sujet, consultez la section Glisser-déposer personnalisé.

Autres ressources