7.10 Exemple Drag and Drop

Écrit par Neil Deakin .
Traduit par Laurent Jouanneau (11/11/2004), mise à jour par Alain B. (04/04/2007) .

Attention : Ce tutoriel est ancien et n'est pas mis à jour. Bien que beaucoup d'informations soient encore valables pour les dernières versions de gecko, beaucoup sont aussi obsolètes. Il est préférable d'aller consulter cette page sur la version française de ce tutoriel sur developer.mozilla.org.

Attention, cette page est maintenue dans la version française, mais elle a été enlevée par son auteur sur la version anglaise.

Un exemple de l'implémentation du glisser-déposer est montré dans cette section.

Glisser-déposer des éléments

Ici, nous créérons un simple panneau où des éléments peuvent être glisser-déposer à partir d'une palette. L'utilisateur peut cliquer sur l'un des nombreux éléments XUL de la palette et les glisser au-dessus d'un élément stack pour créer un élément d'un type particulier.

Tout d'abord, nous ajouterons les scripts du conteneur javascript :

<script src="chrome://global/content/nsDragAndDrop.js"/>
<script src="chrome://global/content/nsTransferable.js"/>

<script src="dragboard.js"/>

Un script supplémentaire dragboard.js est inclus et contiendra le code que nous allons écrire nous-même.

Le panneau sera créé en utilisant un élément stack. Nous utiliserons quelques propriétés de style pour spécifier la largeur et la hauteur de la pile. Sa taille maximale est aussi spécifiée, ainsi elle ne sera pas redimensionnée lorsque de nouveaux éléments y seront déposés.

Le panneau devra répondre à l'événement dragdrop, en créant un élément lorsque l'utilisateur en déposera un dessus.

<stack id="board"
               style="width:300px; height: 300px; max-width: 300px; max-height: 300px"
  ondragover="nsDragAndDrop.dragOver(event,boardObserver)"
  ondragdrop="nsDragAndDrop.drop(event,boardObserver)">
</stack>

Le panneau a juste besoin de répondre aux événements dragdrop et dragover. Nous ajouterons un observateur boardObserver dans le fichier dragboard.js dans un moment.

Ensuite, une palette sera ajoutée sur le côté droit de la fenêtre. Elle contiendra trois boutons, un pour créer de nouveaux boutons, un pour créer des cases à cocher, et un autre pour créer des champs de saisie. Ces boutons répondront à l'événement draggesture et initialiseront une session de glisser-déposer.

<vbox>

<button label="Bouton"
        elem="button" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button label="Case à cocher"
        elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button label="Champ texte"
        elem="textbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
</vbox>

L'objet nsDragAndDrop sera appelé pour faire la plupart du travail. Nous créerons un observateur listObserver qui définira la donnée à transférer. Notez que chaque bouton ici a un attribut supplémentaire elem. Il s'agit d'un attribut inventé. XUL ne le reconnait pas et l'ignorera, mais nous pourrons toujours le récupérer avec la fonction DOM getAttribute. Nous en avons besoin pour savoir de quel type est l'élément à créer lors du glisser-déposer.

Ensuite nous définirons deux observateurs. Premièrement, listObserver qui a besoin d'une fonction pour gérer le démarrage du glisser.

var listObserver = {
  onDragStart: function (evt,transferData,action){
    var txt=evt.target.getAttribute("elem");
    transferData.data=new TransferData();
    transferData.data.addDataForFlavour("text/unicode",txt);
  }
};

Une seule fonction a été définie, onDragStart, et elle sera appelée par l'objet nsDragAndDrop lorsque ce sera nécessaire. La fonction ajoute la donnée à transférer à l'objet transferData. L'attribut elem est récupéré à partir de la cible de l'événement du glisser-déposer. La cible sera l'élément sur lequel le glisser-déposer a commencé. Nous utiliserons la valeur de cet attribut comme donnée pour le glisser.

L'objet boardObserver aura besoin de trois fonctions, getSupportedFlavours, onDragOver et onDrop. La fonction onDrop récupérera la donnée à partir de la session du glisser-déposer et créera un nouvel élément du type approprié.

var boardObserver = {
  getSupportedFlavours : function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("text/unicode");
    return flavours;
  },
  onDragOver: function (evt,flavour,session){},
  onDrop: function (evt,dropdata,session){
    if (dropdata.data!=""){
      var elem=document.createElement(dropdata.data);
      evt.target.appendChild(elem);
      elem.setAttribute("left",""+evt.pageX);
      elem.setAttribute("top",""+evt.pageY);
      elem.setAttribute("label",dropdata.data);
    }
  }
};

La fonction getSupportedFlavours a seulement besoin de retourner une liste de type que la pile peut accepter lors du déposer. Dans notre cas, elle accepte seulement du texte. Nous n'avons pas besoin de faire quelque chose de spécial pour la fonction onDragOver, ainsi aucun code ne sera ajouté dans son corps.

Le gestionnaire onDrop utilise tout d'abord la fonction createElement pour créer un nouvel élément du type stocké dans la session. Ensuite, appendChild est appelée pour ajouter un nouvel élément à la pile, qui est la cible de l'événement. Enfin, nous ajoutons quelques attributs à ce nouvel élément.

La position des éléments dans la pile est déterminée par les attributs left et top. Les valeurs des propriétés pageX et pageY contiennent les coordonnées du pointeur de la souris sur la fenêtre lorsque la dépose a lieu. Ils nous permettent de placer le nouvel élément à la même position que la souris quand le bouton a été relâché. Ce n'est pas tout a fait la bonne méthode puisque nous devons en fait calculer les coordonnées de l'événement relativement à celles de la pile. Mais elle fonctionne ici parce que le panneau est dans le coin en haut à gauche de la fenêtre.

L'attribut label est défini avec la donnée issue du glisser-déposer, ainsi le bouton a un libellé par défaut.

Cet exemple est assez simple. Un changement possible est d'utiliser un type personnalisé pour les données, au lieu du texte. Le problème avec l'utilisation du texte est que si le texte provenant d'un glisser-déposer externe est le mot button, un bouton sera créé sur le panneau. Un type personnalisé signifie que le panneau acceptera uniquement les glisser-déposer en provenance de la palette.

Le code final est montré ci-dessous :

Exemple 7.10.1 : Source

<window title="Composant à déplacer" id="test-window"
  orient="horizontal"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script src="chrome://global/content/nsDragAndDrop.js"/>
<script src="chrome://global/content/nsTransferable.js"/>
<script src="dragboard.js"/>

<stack id="board"
       style="width:300px; height: 300px; max-width: 300px; max-height: 300px"
  ondragover="nsDragAndDrop.dragOver(event,boardObserver)"
  ondragdrop="nsDragAndDrop.drop(event,boardObserver)">
</stack>

<vbox>

<button label="Bouton"
        elem="button" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button label="Case à cocher"
        elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button label="Champ de saisie"
        elem="textbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
</vbox>

</window>

Exemple 7.10.2 : Source

var listObserver = {
  onDragStart: function (evt,transferData,action){
    var txt=evt.target.getAttribute("elem");
    transferData.data=new TransferData();
    transferData.data.addDataForFlavour("text/unicode",txt);
  }
};

var boardObserver = {
  getSupportedFlavours : function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("text/unicode");
    return flavours;
  },
  onDragOver: function (evt,flavour,session){},
  onDrop: function (evt,dropdata,session){
    if (dropdata.data!=""){
      var elem=document.createElement(dropdata.data);
      evt.target.appendChild(elem);
      elem.setAttribute("left",""+evt.pageX);
      elem.setAttribute("top",""+evt.pageY);
      elem.setAttribute("label",dropdata.data);
    }
  }
};

Dans la section suivante, nous allons voir comment créer des arbres.


Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™ sont des marques de la fondation Mozilla.