Après les WebSockets, dans cet article, nous allons voir une autre nouveauté apportée par HTML 5 : le déplacement d'éléments dans la page et le suivi de ceux-ci en javacript.
Pourquoi HTML5 ?
En effet ! On pourrait très bien utiliser divers frameworks tels que jQuery permettent assez simplement d'implémenter du drag & drop d'éléments dans une page. Plusieurs problèmes à cela cependant :
- Vous devenez dépendant du framework jQuery
- L'ergonomie de la chose est assez discutable dès que l'on a des objets un peu complexes (dans mon cas, des tableaux imbriqués, chaque élément pouvant être déplacé dans le tableau de base ou l'un des tableaux fils (ces tableaux fils compris)).
- La lourdeur de la chose
- L'accessibilité de la chose pour les personnes n'ayant pas de souris est également très discutable
C'est pourquoi nous allons chercher à implémenter une fonctionnalité de drag & drop simple en HTML5. Vous pouvez déjà tester ce drag & drop de manière fonctionnelle. Et nous allons voir comment le créer !
Le tag HTML
Nous allons créer dans notre page deux éléments simples qui seront les conteneurs.
<div class="box"></div>
<div class="box></div>
Puis dans le premier conteneur nous allons ajouter un élément qui pourra être déplacé vers le second.
<div class="box"><div class="element" id="first">Hey !</div></div>
Afin de visualiser un peu mieux ceux-ci, nous pouvons ajouter un peu de style à cela :
<style type="text/css">
.box {
border: 1px solid #CCC;
width: 150px;
height: 150px;
float: left;
margin: 10px;
}
.element {
width: 50px;
height: 50px;
background-color: #CCCCCC;
margin: 5px;
padding: 3px
}
</style>
Nous avons maintenant tous les éléments nécessaires pour faire du drag & drop simple dans notre page. Créons un petit peu de dynamisme dans la chose afin de pouvoir les déplacer.
Les évènements
Nous avons accès à plusieurs nouveaux évènements nous permettant de mettre en place notre drag & drop.
Déplacement de l'élément
Un seul d'entre eux sera utile pour le moment cependant : ondragstart. Cet évènement est déclenché dès que vous débutez le drag & drop sur un objet qui le permet.
Pour rendre un élément draggable, nous devons lui ajouter l'attribut draggable="true". Puis dans la boite qui le contient, nous ajoutons l'évènement approprié.
<script type="text/javascript">
function dragStart(event) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData("Text", event.target.getAttribute('id'));
}
</script>
<div class="box" ondragstart="dragStart(event);">
<div id="first" class="element" draggable="true">Hey !</div>
</div>
Analysons un peu ce code.
L'objet dataTransfer
Chacune des deux méthodes utilisées fait partie de dataTransfer. dataTransfer est un nouvel objet de HTML5. Il permet de transférer des données entre les évènements. Ainsi, lorsque dans un évènement, nous modifions une donnée dans dataTransfer, celle-ci sera accessible à tous les autres évènements suivants. Nous pouvons donc sauvegarder des données et les récupérer dans un autre évènement. Ce qui va s'avérer particulièrement pratique pour déplacer le bon objet.
effectAllowed nous permet de définir le type de déplacement que nous autorisons pour ce drag and drop. Les divers déplacements autorisés sont les suivants :
- all - L'élément peut être copié, déplacé et lié.
- copy - L'élément peut être copié.
- copyLink - L'élément peut être copié et lié.
- copyMove - L'élément peut être copié et déplacé.
- link - L'élément peut être lié.
- linkMove - L'élément peut être déplacé et lié.
- move - L'élément peut être déplacé.
- none - L'élément ne peut pas être draggé.
- uninitialized - Valeur par défaut. Le comportement est "move" pour les éléments éditables, "link" pour les ancres et "copy" pour les autres.
setData quant à lui nous permet de définir une valeur à notre dataTransfer. Ici, nous définissons l'identifiant de l'élément. Mais vous pourriez définir ce que vous désirez et qui vous permettra de retrouver cet élément par la suite.
Certains navigateurs, comme Firefox acceptent n'importe quelle valeur. D'autres, comme Chrome en revanche, exigeront que vous utilisiez la bonne.
Notre objet est maintenant déplaçable dans la page. Il nous faut maintenant le dropper dans la seconde boite.
Droppage de l'élément
Se contenter de déplacer l'élément ne va pas nous emmener très loin. Afin de pouvoir dropper l'élément, nous allons ajouter deux évènements : ondragover et ondrop.
ondragover est est exécuté lorsque l'on passe au dessus de l'élément. Il va nous permettre ici d'autoriser ou non le droppage de l'élément dans la seconde boite. Par défaut, javascript n'accepte pas que l'on droppe l'élément. En conséquent on doit retourner false afin d'arrêter la propagation de l'évènement et de ne pas avoir le comportement par défaut (et donc de pouvoir dropper l'élément).
ondrop est exécuté lorsque l'on lache l'élément dans la seconde boite. C'est à ce moment la que nous déplaçons effectivement l'élément dans le dom.
Voici donc à quoi ressemblent nos deux boites maintenant.
<script type="text/javascript">
function dragStart(event) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData("Text", event.target.getAttribute('id'));
}
function dragOver(event) {
return false;
}
function drop(event) {
var element = event.dataTransfer.getData("Text");
event.target.appendChild(document.getElementById(element));
event.stopPropagation();
return false;
}
</script>
<div class="box" ondragstart="dragStart(event);" ondragover="return dragOver(event);" ondrop="return drop(event);">
<div id="first" class="element" draggable="true">Hey !</div>
</div>
<div class="box" ondragstart="dragStart(event);" ondragover="return dragOver(event);" ondrop="return drop(event);">
Essayez par vous même. Vous pouvez maintenant drag/dropper l'élément d'une boite à l'autre ! :)
Tous les évènements disponibles
Nous avons vu ici comment faire quelque chose de très simple. Cependant il vous est possible de complexifier la chose en fonction de vos besoins. Plusieurs évènements sont pour cela à votre disposition.
- dragstart - Evènement représentant le début du déplacement. Est exécuté dès que vous commencez à bouger un élément.
- drag - Evènement exécuté à intervales réguliers lors du déplacement (le référentiel HTML5 indique toutes les 350ms). Si il retourne faux, le déplacement sera arrêté et annulé.
- dragenter - Evènement exécuté lorsque l'élément que vous déplacez arrive dans un autre élément.
- dragleave - Evènement exécuté lorsque l'élément que vous déplacez sors d'un autre élément.
- dragover - Evènement exécuté lorsque vous passez au dessus d'un élément. Si vous laissez cet évènement continuer sa propagation (en ne retournant pas "faux"), il ne sera pas possible de dropper l'objet que vous déplacez dans cet élément.
- drop - Evènement exécuté lorsque vous dropper l'objet que vous êtes en train de déplacer dans un autre élément.
- dragend - Evènement représentant la fin du déplacement. Exécuté lorsque celui-ci s'achève.
Conclusion
A l'heure actuelle, seul Opéra ne supporte pas le drag and drop. Toutes les versions récentes des navigateurs le supportent. Cette solution est bien évidemment à préférer à toute solution intégralement javascript qui ne fera que simuler ce qui est ici geré nativement par le navigateur.


Commentaires
"Toutes les versions récentes des navigateurs le supportent" c'est enfoncer une porte ouverte que de dire que IE ne le supporte pas, y compris la preview d'IE9 ? :)
d'après l'expérience que tu as, est ce qu'il te semblerait facile de construire une librairie JS qui assure la rétro-compatibilité ? Les actuelles se basent sur les event onmousedown, mouseover et mouseup tout en déplaçant des objets DOM en position:absolute à l'endroit où se trouve la souris. Ca te parait compatible avec ce que les browsers ont fait de la spec ?
Bonjour, La démo ne semble pas fonctionner sous Chrome dev en v6. J'ai regardé rapidement le source et je n'ai pas vue d'erreur notoire... Bon article, néanmoins :p
C'est corrigé sous Chrome (et Safari aussi normalement). Voir l'encart rouge dans dataTransfer pour les explications.
Je t'avoue ne pas avoir testé sous IE. Je n'ai aucun windows sous la main et pas la possibilité de le faire donc. Je me suis basé sur les dires de html5demos : http://html5demos.com/
Construire ce genre de librairies me semble faisable en effet oui.
Ca reste effectivement très simple, et ressemble très fortement au DragManager de Flex, j'aime!
Par contre ton exemple fonctionne chez moi avec Firefox mais pas avec Chrome ni Safari...
Je confirme, sous IE cela ne fonctionne pas... mais le billet mérite d'être retwitté. Merci
genial c'est pile poil ce qu'il me fallait en fait je cherche a faire un fond d'ecran avec les elements deplaçable.. petit souscis je suis sous linux et comme fond d'ecran il accepte que les fichiers images.. mais je vais voir la chose.. merci encore.. ah oui petite question c'est quoi ces pubs qui viennent? c'est peut-etre pas toi..
Quelles pub ?? Mis à part les deux Adsense qu'il y a sur cette page, il n'y a aucune autre pub ici.
Merci beaucoup pour ce bon tuto très utile!!
J'ai une petite question : J'ai créé deux listes. Les éléments peuvent ainsi être déplacé d'une liste à l'autre. Par contre lorsque je veux en mettre un au milieu d'une des deux listes, l'élément en question se lie à chaque fois à l'objet du dessus. Pourtant j'ai juste mit 'move' comme 'effectAllowed'. Il existe un moyen pour que ca ne se lie pas??
Encore merci :)
Je ne suis pas sur de comprendre comment tu peut mettre un élément au milieu de deux listes. Cela signifierait que tu en as 3, la 3e étant celle du milieu :)
Hum oui je me suis mal exprimé désolé. En fait je veux déplacer un élément de la liste 1 dans la liste 2. J'arrive à les déplacer à la fin de la liste mais pas au milieu. Je ne sais pas si je suis encore très clair alors voila: j'ai la liste 1 qui contient A,B,C,D et la liste 2 E,F,G,H. Lorsque je veux mettre B entre F et G, B se "colle" à F. Au lieu de simplement se placer entre.
Voici ma triste histoire :)
Merci!!
Oui, je vois. C'est assez simple.
Dans la fonction drop(), on fait un appendChild. C'est l'appel à cette méthode qui ajoute l'élément dans la liste. Il suffit de ne pas faire un appendChild, mais de l'ajouter à l'endroit que tu désire.
Avec jQuery, la méthode after() te permettra de faire cela. Il suffit que tu sache après quel élément tu souhaite insérer le tiens. http://api.jquery.com/after/
Juste une question: si le drag and drop n'est plus fonctionnel sur windows, comment peut alors le restituer ? Merci de répondre
Merci pour ce tuto. Dans le cas d'une longue page Web (avec ascenseur vertical), est-il possible de glisser un élément d'une partie visible de la page vers un target qui n'est pas visible ?
(je reposte, car j'ai l'impression que ça n'a pas marché la première fois)
Pardon mais la démo marche pas du tout chez moi (Firefox 7)...
Up, désolé ça marche très bien en fait. Merci pour le tuto, j'ai un client qui va être content ;-)
Bonjour,
Et merci pour ce tuto.
On est donc limité au déplacement d'une zone a une autre, avec un 'laché' au hazard dans la zone de réception obligatoirement prédefinie. Impossible donc de placer un élément sur la page avec précision? Ce que nous faisions avec javascript avant HTML5 ;o)
C'est plus simple certes! mais bon...
Merci
Merci Damien pour ce tutorial.
J'ai essayé de résoudre le problème de Germain. Je ne l'ai pas testé sur d'autres navigateurs que Chrome mais ca marche.
J'ai crée plusieurs 'element' et 2 'box':
Et j'ai modifié la fonction de drop ainsi:
function drop(event) {
}
Le principe est simple: - lorsque le drop se fait sur une 'box' je l'ajoute a la liste (appendChild). - lorsque le drop se fait sur un 'element' j'insere mon element avant (insertBefore)
PS pour Galaxy, je pense que le déplacement avec précision peut se faire facilement. Bien que je n'ai pas fait de javascript depuis 2 ans, je pense qu'il ne me faudrait pas plus de 2h pour réussir.