HTML5 – Drag & Drop

Curseur main

Ahhh le Drag & Drop ! Cette action si commune et si intuitive qui consiste à faire glisser un élément graphique du bout de sa souris d’ordinateur, pour aller le lâcher au dessus d’une zone d’accueil…

Et bien chers lecteurs j’ai le plaisir de vous apprendre que cette fonctionnalité très appréciée des internautes fait partie intégrante de la spécification HTML5 proposée par le W3C !

Je vous propose d’en découvrir les rudiments à travers un exemple.

Et bien moi, je ne t’ai pas attendu !

Le Drag & Drop, parfois abrégé DnD et appelé glisser-déposer en français, n’est pas une fonctionnalité neuve et n’a pas attendu HTML5 pour exister. Cette fonctionnalité existe sur beaucoup de supports, navigateurs Internet inclus.

Ainsi avant HTML5 et aujourd’hui encore puisque tous les navigateurs n’ont pas complètement implémenté les spécifications du W3C, on glisse à l’aide de frameworks Javascript tels que Mootools ou jQuery.

Le tutoriel qui suit n’a besoin d’aucune bibliothèque de ce genre. Tout le code que vous trouverez est du Javascript natif, et sous réserve que votre navigateur favori ait implémenté le DnD, vous pouvez vous en inspirer !

Apprends-moi

Le DnD repose sur trois points importants :

  1. Des attributs sur les éléments HTML qui glissent et les cibles;
  2. Des gestionnaires d’évènements (event handler en anglais);
  3. Des styles CSS pour rendre le glisser-déposer à la fois visuel et ergonomique.

Le HTML

Les éléments draggables, les éléments qui glissent !

Un élément qui possède la faculté d’être déplacé a un attribut draggable. J’affectionne la syntaxe XHTML donc je renseigne une valeur, ici true, mais en HTML ce n’est pas nécessaire.

<img draggable="true" id="mydraggable" alt="Minion with an apple on the head" src="img/minion_apple.jpg" />

La plupart des moteurs de rendu construisent les liens ainsi que les images comme des éléments draggable.

La dropzone, piste d’atterrissage.

La zone de dépôt a un attribut dropzone. Lors de sa déclaration il est important de savoir quels types de données vont lui être envoyés. La dropzone peut recevoir du texte, ou des données binaires. Il est également important de prévenir l’utilisateur du comportement du dépôt. L’élément glissé peut être copié ou déplacé. Il est également possible de n’envoyer que le lien de cet élément.

<div dropzone="move" id="mydropzone"></div>
Valeur de l’attribut dropzone Sens
copy
L’élément glissé sur le dépôt sera copié.
move
L’élément glissé sur le dépôt sera déplacé. Il n’existera plus où vous l’avez pris.
link
Un lien vers les données de l’élément glissé sera envoyé au dépôt.

Cet attribut ne définit pas l’action réelle du glisser-déposer. C’est à vous de définir son comportement. Théoriquement cet attribut aura une influence sur le type de curseur au survol du dépôt. A la rédaction de ce tutoriel ni Firefox (version 24), ni Chrome (version 29) ni Internet Explorer (version 9) n’adaptent le curseur.
Ce ne sont que les attributs effectAllowed et dropEffect des éléments draggable et dropzone qui provoquent le changement de curseur. Ces attributs sont utilisés dans la partie suivante.

Le Javascript

Tout va se faire ici. HTML5 met à notre disposition sept évènements nous permettant de gérer le glisser-déposer.

Evènement Rôle
dragstart
Début des opérations, l’élément commence à glisser.
drag
Vous déplacez l’élément. Cet évènement est périodique.
dragend
Fin des opérations, vous avez lâché l’élément (dans un dépôt ou ailleurs).
dragenter
Vous venez d’entrer dans « l’espace aérien » d’un dépôt.
dragover
Vous survolez un dépôt. Cet évènement est périodique.
dragleave
Vous venez de quitter « l’espace aérien » d’un dépôt.
drop
Vous avez lâché l’élément sur le dépôt.

Tous ne sont pas indispensables. Les trois évènements dragenter, dragover, et dragleave sont par exemple utiles pour mettre en place des effets visuels.

Je connecte mes éléments draggable

Ici je n’utiliserai que les évènements dragstart et dragend. L’évènement drag ne sera jamais capturé.

La méthode Javascript permettant d’attacher un gestionnaire d’évènement à un objet s’appelle addEventListener. Pour les versions d’Internet Explorer inférieures à 9 il faut encore utiliser attachEvent, ce qui n’est pas fait ici pour simplifier la lecture.

draggable = document.getElementById("mydraggable");

draggable.addEventListener("dragstart", function(event){
	/*
	 * Je viens de démarrer le glisser-déposer, je colore l'arrière plan en vert.
	 * Le vert c'est beau, le vert c'est visuel...
	 */
	event.target.style.background = "green";

	/* En accord avec l'attribut "dropzone" nous autorisons les déplacements */
	event.dataTransfer.effectAllowed = "move";

	/*
	 * Maintenant nous précisons ce qui doit être transmis au dépôt, en cas de
	 * glisser-déposer complet.
	 * Deux possibilités gérées ici :
	 * 1 - notre objet "draggable" est une image et nous envoyons de quoi la
	 * reconstruire dans le dépôt.
	 * 2 - notre objet "draggable" contient du texte brute, alors nous enverrons
	 * ce texte brute au dépôt.
	 */
	var src = event.target.getAttribute("src");
	var alt = event.target.getAttribute("alt");

	if(null != src) /* Présence d'un attribut "src", c'est une image ! Cas n°1 */
		event.dataTransfer.setData("text/html",
				"<img alt="" />");
	else /* C'est autre chose. Cas n°2 */
		event.dataTransfer.setData("text/plain",
				event.target.innerText || event.target.textContent);
});

draggable.addEventListener("dragend", function(event){
	/*
	 * Notre élément a été lâché. Nous pouvons le supprimer puisqu'il était convenu
	 * que notre dépôt fonctionne en déplacement (dropzone="move").
	 *
	 * Bien entendu l'évènement "dragend" est appelé même si l'élément n'a pas été
	 * lâché sur un dépôt. Il nous appartient de détecter cette situation et de
	 * restaurer la couleur de fond en blanc, pour être prêt à recommencer l'opération.
	 *
	 * Ce cas est géré sur le projet de démonstration, ici nous considérons que tout
	 * s'est bien passé.
	 */
	event.target.parentNode.removeChild(event.target);
});

Je connecte ma dropzone

var dropzone = document.getElementById("mydropzone");

dropzone.addEventListener("dragenter", function(event){
	/*
	 * On survole notre dépôt, je colore le dépôt en gris clair pour indiquer à
	 * l'utilisateur qu'il peut lâcher ici.
	 */
	event.target.style.background = "#CCCCCC";
	event.preventDefault();
});

dropzone.addEventListener("dragleave", function(event){
	/* L'utilisateur quitte le dépôt, on repasse sur un fond blanc. */
	event.target.style.background = "white";
});

dropzone.addEventListener("dragover", function(event){
	/*
	 * En accord avec l'attribut "dropzone" nous acceptons les "draggable" en
	 * déplacement.
	 */
	event.dataTransfer.dropEffect = "move";

	/*
	 * Ici il est particulièrement important d'écraser le comportement par
	 * défaut des navigateurs, qui consiste à refuser toutes les opérations.
	 */
	event.preventDefault(); /* Normalement cette instruction suffit. */
	return false; /* Celle-ci est pour les navigateurs réfractaires. */
});

dropzone.addEventListener("drop", function(event){
	/*
	 * L'utilisateur a lâché son objet sur notre dépôt !
	 *
	 * Nous allons le déplacer de son emplacement d'origine vers l'élément "mylist"
	 * (une simple division)
	 */
	var mylist = document.getElementById("mylist");

	/*
	 * Deux possibilités :
	 * 1 - les données "text/html" sont renseignées donc c'est une image
	 * 2 - les données "text/plain" sont renseignées donc c'est du texte brut, que nous
	 * allons afficher dans un élément <span>.
	 */
	var html = event.dataTransfer.getData("text/html");
	if("" != html) //Cas n°1
{
	mylist.innerHTML += html;
}
else //Cas n°2
{
	var span = document.createElement("span");
	span.innerHTML = event.dataTransfer.getData("text/plain");
	mylist.appendChild(span);
}

/*
 * Comme l'utilisateur ne va pas provoquer l'évènement "dragleave" on repasse
 * sur un fond blanc.
 */
event.target.style.background = "white";

event.preventDefault(); /* (Firefox redirigerait la page sans cette instruction.) */
});

Certains navigateurs anticipent vos besoins et gèrent des transferts d’information spécifiques. Par exemple Firefox gère les transferts de données sur les images, il n’est alors pas besoin de préciser ce qui doit être échangé sur les éléments img.

Le CSS

Même si comme je vous le disais tout le travail (ou presque) est réalisé par Javascript, c’est visuellement que votre glisser-déposer va faire naître des étoiles dans les yeux de vos internautes ! Il est donc important de rendre votre glisser-déposer très visuel, très ergonomique, bref, sexy.

Des couleurs, une transition et un curseur, voici les styles que je vous propose d’ajouter. Vous verrez que ceci constitue une base très convenable, et que l’internaute lambda saura s’y retrouver. Les changements de couleurs de fond sont réalisés directement dans Javascript, en réponse aux évènements qui nous intéressent. Nous allons donc nous concentrer sur la manière dont ces changement sont réalisés.

Pour simplifier la lecture des styles ces derniers ne sont volontairement pas déclinés dans leurs versions de travail. (Les navigateurs qui n’ont pas encore intégré certains styles les proposent parfois en essai. Pour informer le navigateur que nous voulons un style encore en travaux il faut utiliser le préfixe du moteur de rendu concerné.)
Le site caniuse.com propose un tableau récapitulatif qui vous permettra de savoir si un navigateur donné supporte une propriété CSS3 en particulier. Vous saurez par la même occasion s’il est nécessaire d’utiliser la propriété préfixée dans vos feuilles de style.

Les styles des éléments draggable

[draggable=true]{
	cursor: move;
}
[draggable=true]:hover{
	box-shadow:0 0 5px green; /* CSS3 */
}

Le sélecteur [draggable=true] est disponible dans la version 3 de CSS. Tous les éléments possédant l’attribut draggable avec la valeur true seront ciblés.

Nous pouvons utiliser les mots draggable et dropzone (voir style suivant) puisque nos éléments <img> et <div> possèdent ces attributs. Ce sélecteur n’est pas lié à l’API HTML5 Drag & Drop. Il peut fort bien être utilisé avec par exemple l’attribut src des images ou href des liens.

Ici nous remplaçons, lors du survol de l’élément, le curseur de souris par défaut (la flèche), par le curseur de déplacement (les quatre flèches disposées en croix). Comme rien n’est imposé j’ai aussi choisi de créer un léger halo vert, grâce à la propriété box-shadow.

Les styles de l’élément dropzone

Un seul style, totalement facultatif, pour rendre les changements de couleur plus doux. Cette propriété est également donnée aux éléments draggable, qui changent eux aussi de couleur de fond, et qui possèdent un halo.

[draggable=true], [dropzone]{
	transition:
		box-shadow linear 0.2s,
		background-color linear 0.2s;
}

Conclusion

De mon ressenti la fonctionnalité HTML5 Drag & Drop a été bien pensée et s’avère au moins aussi simple à implémenter qu’avec des frameworks Javascript comme Mootools, que j’utilisais jusqu’à aujourd’hui.

Comme vous l’avez compris beaucoup d’éléments sont mis à la disposition du développeur pour maîtriser toute la chaîne d’opérations. Cependant il vous appartient d’en faire usage ou non, compte-tenu de vos besoins.

Un exemple complet est disponible sur le projet GitHub SoDnD. Pour tester en live ça se passe sur JSFiddle !

Pour savoir si votre navigateur implémente le glisser-déposer, c’est encore sur caniuse.com.

HTML5 ne s’est pas arrêté là et spécifie également le glisser-déposer de fichiers depuis le système d’exploitation vers le navigateurs (drag-in), ainsi que l’action inverse, le glisser-déposer d’éléments depuis le navigateur vers l’extérieur (drag-out). Le présent article ne traite pas de ces deux points qui sont deux fonctionnalités avancées et encore très neuves pour les navigateurs actuels.

Nombre de vue : 1536

COMMENTAIRES 1 commentaire

  1. lm dit :

    Bonjour,
    Le pb du drag and drop c’est le fonctionnement sous ie.
    Je n’ai pas encore trouvé une seule démo qui fonctionne complètement sous ie (sous Chrome et Firefox ça va très bien en général).
    Je viens de tester votre exemple via jsFiddle sous ie11 et ça ne marche pas bien (l’affichage des draggables dans la partie droite ne se fait pas).
    Avez-vous une idée de solution ?

AJOUTER UN COMMENTAIRE