HTML5 - L'attribut draggable
Après avoir évoqué l'attribut contentEditable
d'HTML5, je vous propose cette fois de nous intéresser à l'API de drag and drop de cette prochaine version.
Présentation
L'attribut draggable
peut être associé à n'importe quel élément HTML et placé à trois valeurs: 'false' (par défaut) , 'true' afin de rendre le déplacement possible et enfin 'auto' dont le comportement dépendra du navigateur. Il représente la mise en application de l'API de drag and drop d'HTML5.
Avec cet API et cet attribut, il existe un certain nombre d'évènements qui sont déclenchés par les actions de l'utilisateur et qui peuvent être écoutés pour exécuter des actions:
-
dragstart
(ondragstart
pour l'attribut HTML): qui se déclenche lorsqu'un drag and drop d'une zone est commencé. -
drag
(ondrag
pour l'attribut HTML): déclenché pendant le déplacement (et donc potentiellement à répétition...). -
dragenter
(ondragenter
pour l'attribut HTML): déclenché lorsqu'un drag and drop entre au dessus de la zone concernée. -
dragleave
(ondragleave
pour l'attribut HTML): déclenché lorsqu'un drag and drop quitte la zone concernée. -
dragover
(ondragover
pour l'attribut HTML): déclenché lorsqu'un drag and drop se situe au dessus de la zone concernée (et donc potentiellement à répétion...). -
drop
(ondrop
pour l'attribut HTML): déclenché lorsqu'un élément en cours de déplacement est relaché au dessus de la zone concernée. -
dragend
(ondragend
pour l'attribut HTML): déclenché lorsqu'un drag and drop a été réalisé ou annulé.
La preuve par l'exemple
Scénario de l'exemple
Afin de rendre tout ça plus "buvable" nous allons utilisez pour l'exemple un scénario très simple basé sur ces évènements et ces actions:
- L'utilisateur commence le déplacement d'une "boite" depuis une source de contenu représentée par une plus grosse "boite". Du coup l'évènement "dragstart" est déclenché pour cette boite. On ne s'occupera pas de l'évènement "drag" pour la suite.
- Il la déplace sur la page et arrive au dessus de la "boite" cible. L'évènement "dragenter" est déclenché par la cible.
- En déplaçant la petite "boite" au dessus de la cible, cette dernière déclence des évènements "dragover".
- L'utilisateur relâche la petite "boite" sur la cible qui déclenche l'évènement "drop". Cet évènement nous servira de référence pour retirer l'objet déplacé de sa source et le placer dans la cible de façon concrête.
Le code HTML
Nous allons créer 2 div
qui nous serviront de source ('src') ou de cible ('tgt'). À l'intérieur de la boite 'src' nous aurons une petite boite nommée 'box' qui pourra être déplacée dans 'src'.
<!-- just a container --> <div class="bordered"> <!-- our drag source container --> <div class="half-width source" id="src"> <!-- our box to drag and drop --> <div class="box" id="box" draggable="true"> Simple Box 1 </div> </div> <!-- our drag and drop target --> <div class="half-width target" id="tgt"></div> </div>
Rien de plus simple jusque là. À noter toutefois, l'utilisation de l'attribut draggable
à la valeur 'true' pour autoriser le déplacement de la boite 'box'.
CSS
Juste un peu de mise en forme et un 'hack' pour le moteur WebKit (Safari entre autres) nécessaire pour activer le drag and drop (cette situation ne saurait être définitive je pense car elle ne respecte pas la norme HTML5.)
.bordered { border: 1px solid #888888; } .half-width { float:left; width:50%; min-height:160px; } .target { background-color:#BFFF80; } .source { background-color:#FFDF80; } .box { background-color:#FF8080; margin:5px; padding:5px; border: 1px solid #888888; height:100px; width:100px; text-align:center; } *[draggable=true] { -khtml-user-drag: element; /* WebKit hack */ cursor:move; /* a move cursor */ -moz-user-select:none; /* disabled content selection on mozilla browsers */ }
dragstart
Comme expliqué plus haut, l'évènement dragstart
se déclenche lorsqu'un drag and drop d'une zone est commencé. Nous allons donc associer à l'évènement dragstart
de la boite à déplacer, la création de notre drag and drop.
/* on associe le démarrage du drag and drop à la fonction dragStartHandler */ document.getElementById('box').addEventListener("dragstart", dragStartHandler, false); function dragStartHandler(event) { /* autoriser les drag and drop de type "copy" */ event.dataTransfer.effectAllowed = 'copy'; /* transmettre en donnée de drag and drop l'id de la boite déplacée */ event.dataTransfer.setData('Text', event.target.id); return false; }
dragenter
Cette fois-ci nous allons associer une fonction javascript à l'entrée dans la zone cible:
/* on associe l'entrée du drag and drop sur la cible à la fonction dragEnterHandler */ document.getElementById('tgt').addEventListener("dragenter", dragEnterHandler, false); function dragEnterHandler(event) { /* éviter le comportement par défaut du navigateur (déplacement de sélection) */ event.preventDefault(); return false; }
dragover
Ici nous nous intéressons à ce qu'il se passe lorsqu'on survole la cible. Conformément à ce que nous avons mis dans la fonction dragStartHandler
nous allons donner au déplacement la zone la capacité à recevoir les drag and drop de type 'copy':
/* on associe le survole de la cible à la fonction dragOverHandler */ document.getElementById('tgt').addEventListener("dragover", dragOverHandler, false); function dragOverHandler(event) { /* verification de la cible au cas où l'on survole un autre objet */ if (event.target != document.getElementById('tgt')) { return false; } else { /* on attribue l'effet de curseur qui correspond au type accepté plus haut */ event.dataTransfer.dropEffect = 'copy'; /* éviter le comportement par défaut du navigateur (déplacement de sélection) */ event.preventDefault(); } return false; }
drop
Nous y voilà. L'étape finale du déplacement et l'action de "relaché" plus communément appelée "drop" va être connectée à une fonction qui déplacera la zone déplacée dans la cible.
/* on associe le 'laché' d'un élément sur la cible à la fonction dropHandler */ document.getElementById('tgt').addEventListener("drop", dropHandler, false); function dropHandler(event) { /* on se sert de l'id transporté en donnée pour déplacer l'élément dans son nouveau parent DOM */ event.target.appendChild(document.getElementById(event.dataTransfer.getData('Text'))); /* et voila, on a finit et pour s'assurer on coupe manuellement la propagation de l'évènement (pas forcément nécessaire !) */ event.stopPropagation(); return false; }
Le résultat
Voila donc notre exemple prêt à marcher :