Glisser et déposer

L'interface HTML Drag and Drop (pour glisser-déposer) permet à des applications d'utiliser des fonctionnalités de glisser-déposer dans le navigateur.

L'utilisateur pourra sélectionner des éléments déplaçables à la souris et les déplacer vers un élément où on peut déposer en relâchant le bouton de la souris. Une représentation translucide de l'élément déplacé suit le pointeur lors de l'opération.

Pour les sites web et les extensions, on peut personnaliser les éléments qui peuvent être déplacés, la façon dont ceux-ci sont signalés et les éléments qui peuvent servir de destination.

L'aperçu de cette API inclut une description des interfaces, les étapes à suivre pour prendre en charge ces fonctionnalités dans une application et un aperçu de l'interopérabilité de ces interfaces.

Évènements de déplacement

L'API HTML Drag and Drop utilise le modèle d'évènements du DOM (Event) ainsi que les éléments de déplacements (DragEvent) hérités des évènements liés à la souris (MouseEvent). Une opération de déplacement commence généralement lorsqu'un utilisateur sélectionne un élément déplaçable puis qu'il le déplace sur un élément de destination avant de relâcher l'élément déplacé.

Lors des opérations de déplacement, plusieurs évènements sont déclenchés (dont certains qui sont déclenchés à plusieurs reprises comme drag et dragover).

Chaque type d'évènement de déplacement possède un gestionnaire d'évènement global (une méthode on...) :

Évènement Gestionnaire d'évènement global Déclenchement
drag ondrag …un objet déplaçable (que ce soit un élément ou une sélection de texte) est déplacée.
dragend ondragend …une opération de déplacement se termine (en relâchant le bouton de la souris ou en utilisant la touche Echap, voir Terminer un déplacement)
dragenter ondragenter …un élément en cours de déplacement arrive sur une zone de dépôt valide (voir indiquer une cible de destination).
dragexit ondragexit …un élément n'est plus la sélection immédiate du déplacement.
dragleave ondragleave …un élément en cours de déplacement quitte une zone de dépôt valide.
dragover ondragover …un élément en cours de déplacement est en cours de survol d'une zone de dépôt valide (cet évènement est déclenché toutes les quelques centaines de millisecondes).
dragstart ondragstart …l'utilisateur commence à déplacer un élément (voir démarrer une opération de glissement).
drop ondrop …un élément est déposé sur une cible valide (voir déposer un élément).

Note : Les évènements dragstart et dragend ne sont pas déclenchés lors qu'on glisse-dépose un fichier de l'appareil dans le navigateur.

Interfaces

Les interfaces fournies par cette API sont

L'interface DragEvent possède un constructeur et une propriété dataTransfer qui est un objet DataTransfer.

Les objets DataTransfer incluent l'état du glisser-déposer, le type de déplacement (copy ou move), les données déplacées (un ou plusieurs objets) et le type MIME de chaque objet déplacé. Les objets DataTransfer possèdent également des méthodes permettant d'ajouter ou de retirer des objets aux données déplacées.

Les interfaces DragEvent et DataTransfer sont standard et suffisent à apporter des fonctionnalités de glisser/déposer. Toutefois, Firefox prend en charge quelques extensions spécifiques à Gecko (cf. ci-après) pour l'objet DataTransfer (bien entendu, ces extensions ne fonctionneront que dans Firefox et pas dans les autres navigateurs).

Chaque objet DataTransfer possède une propriété items qui est une liste (list) d'objets DataTransferItem. Un objet DataTransferItem représente un seul objet déplacé, avec une propriété kind qui indique s'il s'agit d'un texte (string) ou d'un fichier (file) et une propriété type qui correspond au type MIME de la donnée déplacée. L'objet DataTransferItem possède également des méthodes pour consulter les données de l'objet déplacé.

L'objet DataTransferItemList est une liste d'objets DataTransferItem. La liste possède des méthodes pour ajouter un objet en déplacement à la liste, pour retirer un objet de la liste ou pour vider la liste de tout ses objets.

La différence principale entre DataTransfer et DataTransferItem est l'utilisation de la méthode synchrone getData() pour la première et de la méthode asynchrone getAsString() pour la deuxième.

Note : DragEvent et DataTransfer sont largement prises en charge par les navigateurs de bureau tandis que DataTransferItem et DataTransferItemList ont une compatibilité plus restreinte. Voir la section ci-après sur l'interopérabilité.

Interfaces spécifiques à Gecko

Mozilla / Firefox prend en charge certaines fonctionnalités qui ne font pas partie du modèle standard. Ce sont des fonctions utilitaires pour aider au déplacement de plusieurs objets ou de données qui ne sont pas du texte (des fichiers par exemple). Pour plus d'informations, voir Glisser-déposer plusieurs objets. Voir aussi la page de référence de DataTransfer pour la liste de l'ensemble des propriétés spécifique à Gecko et des méthodes spécifiques à Gecko.

Bases

Dans cette section, nous allons voir les premières étapes nécessaires aux fonctionnalités de glisser-déposer dans une application.

Identifier ce qui peut être déplacé

Pour qu'un élément puisse être déplacé, il faut lui ajouter l'attribut draggable ainsi que le gestionnaire d'évènement global ondragstart :

html
<script>
  function dragstart_handler(ev) {
    // On ajoute l'identifiant de l'élément cible à l'objet de transfert
    ev.dataTransfer.setData("text/plain", ev.target.innerText);
  }
</script>

<p id="p1" draggable="true" ondragstart="dragstart_handler(event)">
  Cet élément est déplaçable.
</p>

Voir la page de référence sur l'attribut draggable et le guide sur les opérations de déplacement pour plus d'informations.

Définir les données déplacées

Une application peut inclure plusieurs objets dans une opération de glisser/déposer. Chaque objet est une chaîne de caractères (DOMString) ayant un type MIME particulier (indiqué par son attribut type) tel que text/html.

Chaque DragEvent possède une propriété dataTransfer contenant les données transportées. Cette propriété (un objet DataTransfer) possède des méthodes pour gérer les données transportées. La méthode setData() permet d'ajouter un objet aux données transportées :

js
function dragstart_handler(ev) {
  // On ajoute différents types de données transportées
  ev.dataTransfer.setData("text/plain", ev.target.innerText);
  ev.dataTransfer.setData("text/html", ev.target.outerHTML);
  ev.dataTransfer.setData(
    "text/uri-list",
    ev.target.ownerDocument.location.href,
  );
}

Pour connaître la liste des types de donnée communément utilisées lors d'un glisser/déposer (texte, HTML, liens, fichiers, etc.), voir les types recommandés. Pour plus d'informations sur les informations transportées, voir Drag Data.

Définir l'image pour le déplacement

Par défaut, le navigateur fournit une image qui apparaît à côté du pointeur lors de l'opération de déplacement. Toutefois, une application peut définir une image personnalisée grâce à la méthode setDragImage() :

js
function dragstart_handler(ev) {
  // On crée une image qu'on utilise pour le déplacement
  // Note : on changera "example.gif" vers une vraie image
  // (sinon l'image par défaut sera utilisée)
  var img = new Image();
  img.src = "example.gif";
  ev.dataTransfer.setDragImage(img, 10, 10);
}

Pour en savoir plus, voir Définir l'image de feedback pour le glisser-déposer.

Définir l'effet de déplacement

La propriété dropEffect est utilisée pour fournir un retour à l'utilisateur qui effectue l'opération de glisser/déposer. Généralement, cela se traduit par la modification du curseur affiché par le navigateur lors du déplacement.

Il est possible de définir trois effets :

  • copy : indique que les données déplacées seront copiées depuis l'emplacement source vers la cible.
  • move : indique que les données déplacées seront déplacées depuis l'emplacement source vers la cible.
  • link : indique qu'une relation ou une connexion sera créée entre la source et la cible.

Lors de l'opération de déplacement, les effets peuvent être modifiés afin d'indiquer que certains effets sont autorisés à certains emplacements.

Voici un exemple illustrant l'utilisation de cette propriété.

js
function dragstart_handler(ev) {
  ev.dataTransfer.dropEffect = "copy";
}

See Drag Effects for more details.

Définir la zone où déposer l'élément déplacé

Par défaut, le navigateur empêche de déposer quoi que ce soit sur la plupart des éléments HTML. Pour modifier ce comportement, il faut qu'un élément devienne une zone cible ou qu'il soit identifié comme "droppable". L'élément doit avoir les deux gestionnaires d'évènements ondragover et ondrop comme attributs. Dans l'exemple suivant, on montre comment utiliser ces attributs et on fournit des gestionnaires d'évènements simples associés :

html
<script>
  function dragover_handler(ev) {
    ev.preventDefault();
    ev.dataTransfer.dropEffect = "move";
  }
  function drop_handler(ev) {
    ev.preventDefault();
    // On récupère l'identifiant de la cible et on ajoute l'élément déplacé au DOM de la cible
    var data = ev.dataTransfer.getData("text/plain");
    ev.target.appendChild(document.getElementById(data));
  }
</script>

<p
  id="target"
  ondrop="drop_handler(event)"
  ondragover="dragover_handler(event)">
  Zone pour déposer
</p>

On voit ici que chaque gestionnaire invoque preventDefault() afin d'éviter toute gestion d'évènement ultérieure (comme les évènements tactiles ou les évènements de pointeur).

Pour plus d'information, voir Indiquer une cible pour un glisser-déposer.

Gérer le dépôt de l'objet

Le gestionnaire de l'évènement drop permet de gérer les données déposées avec la logique de l'application. Généralement, une application utilisera getData() afin de récupérer les données déplacées et les traitera. L'application peut choisir d'avoir un comportement différent selon la valeur de dropEffect et/ou celles des autres propriétés.

Dans l'exemple suivant, on montre un gestionnaire pour le dépot de l'objet : on récupère l'identifiant (id) de l'élément déplacé puis on utilise celui-ci afin de le déplacer depuis la source vers la cible :

html
<script>
  function dragstart_handler(ev) {
    // On ajoute l'identifiant de l'élément cible à l'objet de transfert
    ev.dataTransfer.setData("application/my-app", ev.target.id);
    ev.dataTransfer.dropEffect = "move";
  }
  function dragover_handler(ev) {
    ev.preventDefault();
    ev.dataTransfer.dropEffect = "move";
  }
  function drop_handler(ev) {
    ev.preventDefault();
    // On obtient l'identifiant de la cible et on ajoute l'élément déplacé
    // au DOM de la cible
    var data = ev.dataTransfer.getData("application/my-app");
    ev.target.appendChild(document.getElementById(data));
  }
</script>

<p id="p1" draggable="true" ondragstart="dragstart_handler(event)">
  Cet élément peut être déplacé.
</p>
<div
  id="target"
  ondrop="drop_handler(event)"
  ondragover="dragover_handler(event)">
  Zone pour le dépôt
</div>

Pour plus d'information, voir Gérer le dépôt lors d'une opération de glisser-déposer.

Terminer l'opération de glisser/déposer

À la fin de l'opération, c'est l'évènement dragend qui est déclenché sur l'élément source (celui qui a été "saisi" au début). Cet évènement est déclenché lorsque l'opération est terminée ou qu'elle a été annulée. Le gestionnaire d'évènement pour dragend peut vérifier la valeur de la propriété dropEffect afin de déterminer si l'opération a réussi ou non.

Pour plus d'informations sur la gestion de la fin d'une opération de glisser-déposer, voir Terminer un glisser-déposer.

Interopérabilité

Comme on peut le voir dans le tableau de compatibilité pour l'interface DataTransferItem, la prise en charge du drag-and-drop est assez répandue parmi les navigateurs de bureau à l'exception des interfaces DataTransferItem et DataTransferItemList. Ce tableau montre également que la prise en charge sur mobile est assez faible.

Exemples et démos

Voir aussi