xulfr.org

8.6 Conteneur JavaScript pour le Glisser-Déposer

Écrit par Neil Deakin. Traduit par Laurent Jouanneau (11/11/2004).
Page originale : http://www.xulplanet.com/tutorials/xultu/dragwrap.html xulplanet.com

Cette section décrit l'utilisation d'un conteneur JavaScript pour le glisser-déposer.

Le conteneur JavaScript Glisser-Déposer

Le conteneur JavaScript pour le glisser-déposer simplifie le processus en appelant toutes les interfaces XPCOM pour vous. Il fonctionne en fournissant un objet qui implémente les gestionnaires d'évènements. Tout ce que vous avez à faire est d'écrire quelques fonctions simples qui travaillent sur les données qui sont glissées.

L'interface Glisser-déposer est stockée dans le paquetage "global", dans le fichier chrome://global/content/nsDragAndDrop.js. Vous pouvez inclure ce fichier dans votre fichier XUL avec la balise script de la même manière que pour vos scripts. La bibliothèque dépend aussi d'autres scripts, que vous aurez également à inclure, habituellement au début de votre page XUL. Vous pouvez regarder le contenu de ces fichiers pour voir comment fonctionne le glisser-déposer au plus bas niveau.

Notez que vous ne pouvez utiliser ces bibliothèques qu'à l'intérieur de fichiers XUL chargés avec un URL chrome.

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

Cette bibliothèque glisser-déposer crée un objet stocké dans la variable nsDragAndDrop. L'objet contient une série de fonctions, une pour chaque gestionnaire d'évènements (excepté pour dragenter où il n'y a rien de spécial à faire). Chacune de ces fonctions prend deux arguments : le premier est l'objet event et le deuxième est un objet observateur que vous créez. Vous aurez plus d'explications plus tard.

L'exemple suivant est un exemple d'appel de l'objet nsDragAndDrop :

<button label="Glissez moi" ondraggesture="nsDragAndDrop.startDrag(event,buttonObserver);" />

La fonction startDrag sera appelée quand le glisser-déposer débutera à partir du bouton. Le premier paramètre est l'objet event, disponible dans tous les gestionnaires d'évènements. Le second paramètre à cette fonction est l'observateur, que nous créerons bientôt. Dans cet exemple, nous ne faisons rien de spécial d'autre quand débute le glisser du bouton. Si nous voulions prendre aussi en compte les autres cas, nous pourrions appeler les autres fonctions, comme dans l'exemple suivant :

<description value="Cliquez et glissez ce texte."
    ondraggesture="nsDragAndDrop.startDrag(event,textObserver)"
    ondragover="nsDragAndDrop.dragOver(event,textObserver)"
    ondragexit="nsDragAndDrop.dragExit(event,textObserver)"
    ondragdrop="nsDragAndDrop.drop(event,textObserver)"/>

Comme mentionné plus haut, il n'y a rien à faire de spécial pendant l'évènement dragenter, aussi vous pouvez l'écrire vous même.

Les fonctions sont implémentées par l'objet nsDragAndDrop, qui est déclaré dans le fichier nsDragAndDrop.js, inclus par l'une des balises script. Elles prennent en charge les évènements, les appels aux interfaces XPCOM, et passent une structure de donnée simple aux fonctions de l'objet observateur.

L'observateur est un objet que vous déclarez vous-même. Dans les exemples ci-dessus, cet observateur est stocké dans les variables buttonObserver et textObserver. L'observateur est déclaré dans un script que vous devez inclure dans votre fichier XUL avec la balise script. Il doit avoir un certain nombre de propriétés, chacune s'occupant d'un aspect particulier du glisser-déposer. Cinq fonctions peuvent être définies. Vous avez juste à définir celle dont vous avez besoin.

onDragStart (event , transferData, action)
Définissez cette fonction pour déclencher une action quand le glisser commence. Elle prend trois arguments : l'objet event qui a été passé au gestionnaire d'évènement; les données à transférer; le type d'action du glisser. Cette fonction doit ajouter les données à transférer à l'objet transferData.
onDragOver (event, flavour, session)
Cette fonction doit être définie quand vous voulez que quelque chose arrive quand le glisser passe au dessus de l'élément. Le premier argument est l'objet event, le second est le type de donnée et le troisième est l'objet de session du glisser, qui fourni plus de détails sur le glisser-déposer en cours. Vous devez définir cette fonction pour les éléments qui autorisent la dépose de données "glissées" sur eux-même.
onDragExit (event, session)
Cette fonction doit être définie quand quelque chose arrive lorsque le glisser quitte l'élément. Elle a deux arguments, l'objet event et la session du glisser-déposer.
onDrop (event, dropData, session)
Cette fonction doit être définie quand vous voulez faire quelque chose lorsque l'objet est déposé. Le premier argument est l'objet event et le second est la donnée qui était glissée. Le troisième argument est la session du glisser-déposer.
getSupportedFlavours ( )
Cette fonction doit retourner la liste des types de données que peut accepter l'objet sur lequel on fait le glisser. Cette fonction ne prend pas d'arguments. Elle est nécessaire car ainsi le conteneur peut déterminer le meilleur type de donnée à passer aux autres fonctions.

Pour un observateur lié à un élément qui peut débuter un glisser-déposer, vous devriez définir au moins la fonction onDragStart. Pour les éléments qui peuvent recevoir des objets glissés, vous devriez définir onDragOver, onDrop et getSupportedFlavours (et si vous le voulez, onDragExit).

Le type des données pouvant être glisser-déposer, est stocké comme un ensemble de type. Souvent, un objet glissé peut être disponible dans un certain nombre de type. Ce faisant, un élément cible peut accepter le type qu'il trouve le mieux adapté. Par exemple, un fichier peut être transmis dans deux types, le fichier lui même et son nom. Si le fichier est glissé et déposé sur un répertoire, le type fichier sera utilisé. Si le fichier est glissé sur un champs de saisie, le type 'nom de fichier' sera utilisé. Le texte du nom du fichier est par conséquent utilisé quand les fichiers ne peuvent être déposés directement.

Un type d'objet a un nom, qui est formaté comme un type MIME, comme text/unicode. À l'intérieur de la fonction onDragStart, vous spécifiez quels types sont disponibles pour l'item en cours de glisser-déposer. Pour faire cela, ajoutez les données et les types à l'objet transferData, qui est le second argument de onDragStart.

L'exemple ci-après devrait vous aider. La fonction onDragStart ajoute des données à l'objet transferData.

var textObserver = {
  onDragStart: function (evt , transferData, action){
    var htmlText="<strong>Cabbage</strong>";
    var plainText="Cabbage";

    transferData.data=new TransferData();
    transferData.data.addDataForFlavour("text/html",htmlText);
    transferData.data.addDataForFlavour("text/unicode",plainText);
  }
};

Ici, un observateur a été déclaré et stocké dans la variable textObserver. Il a une propriété appelée onDragStart (En JavaScript, les propriétés peuvent être déclarées avec la syntaxe nom : valeur). Cette propriété est une fonction qui définit les données qui seront transférées.

Une fois appelé, il commence le glisser-déposer pour la chaîne Cabbage. Bien sûr, vous voudriez calculer cette valeur à partir de l'élément sur lequel on a cliqué. Cet élément est disponible dans la propriété target de l'objet event. Cet objet event est passé en premier argument à onDragStart.

Nous créons un objet transferData qui peut être utilisé pour contenir toutes les données à transférer. Nous ajoutons deux données à celles-ci. La première est une chaîne de texte HTML et la seconde est une chaîne de texte brut. Si l'utilisateur dépose sur une zone qui accepte le HTML (comme la fenêtre d'édition HTML de Mozilla), le type HTML sera utilisé et le texte apparaîtra en gras. Sinon, la version texte brut sera utilisée à la place.

En général vous devrez fournir une version texte de la donnée, ainsi de nombreuses applications pourront l'accepter. L'ordre dans lequel vous définissez les types devra s'établir de la meilleure correspondance vers la moins bonne. Dans le cas ci-dessus, le type HTML (text/html) vient en premier, et le type texte (text/unicode) en second.

L'exemple ci-dessous montre comment spécifier les données à transférer à partir de l'attribut label de l'élément. Dans ce cas, nous fournissons la donnée dans un seul type.

var textObserver = {
  onDragStart: function (evt){
    var txt=evt.target.getAttribute("label");

    transferData.data=new TransferData();
    transferData.data.addDataForFlavour("text/unicode",txt);
  }
}

Cela peut être utile lors de l'implémentation du glisser-déposer pour les cellules d'un arbre. Vous pouvez utiliser la valeur d'une cellule, ou d'une ressource du fichier RDF si l'arbre est construit à partir d'un gabarit, comme valeur pour le glisser-déposer. Si vous la stockez dans une chaîne, n'importe quel objet acceptant les chaînes pour un glisser-déposer, peut récupérer cette valeur.

Vous aurez besoin d'ajouter un observateur à chaque élément qui peuvent soit démarrer une action glisser-déposer, soit accepter des objets glissés. Vous pouvez réutiliser le même observateur sur plusieurs éléments. Pour un élément qui démarre un glisser-déposer, onDragStart est juste ce qu'il faut à implémenter.

Pour un élément sur lequel on peut déposer, l'observateur aura besoin d'implémenter au moins les fonctions getSupportedFlavours, onDragOver et onDrop. Certains éléments pourraient être capable d'initier un glisser et d'accepter un déposer. Dans ce cas, onDragStart sera aussi nécessaire.

La fonction getSupportedFlavours doit retourner une liste de type que peut accepter pour une dépose l'élément sur lequel le glisser-déposer s'effectue. Une vue d'un répertoire de système de fichier pourrait accepter des fichiers et peut-être du texte, mais ne devrait pas accepter du texte HTML. Ci-dessous, nous définissons la fonction getSupportedFlavours. Nous n'autorisons qu'un seul type ici.

var textObserver = {
  getSupportedFlavours : function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("text/unicode");
    return flavours;
  }
}

La liste de type de donnée contient un seul type, qui est text/unicode. L'objet FlavourSet peut être utilisé pour contenir une liste de type. Dans certains cas, vous devez aussi fournir une interface XPCOM. Par exemple, pour les fichiers :

var textObserver = {
  getSupportedFlavours : function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("application/x-moz-file","nsIFile");
    flavours.appendFlavour("text/unicode");
    return flavours;
  }
}

La fonction onDragOver définit ce qui arrive lorsqu'un objet est glissé au dessus. Vous pourriez alors changer l'apparence des éléments qui sont survolés. Dans la plupart des cas, la fonction ne fait rien. Cependant elle doit être définie pour les éléments qui acceptent des données glissées.

Ensuite, la fonction onDrop doit être créée. Son second argument est l'objet de transfert de données qui contient les données transférées. Avant d'appeler onDrop, le conteneur aura appelé getSupportedFlavours pour déterminer le meilleur type de donnée à déposer, aussi l'objet de transfert ne contient que les données du meilleur type déterminé.

L'objet de transfert a deux propriétés : data qui contient la donnée et flavour qui contient le type de la donnée. Une fois que vous avez la donnée, vous pouvez l'ajouter à l'élément de n'importe quelle façon. Par exemple, vous pourriez modifier la valeur d'un champs de saisie.

var textObserver = {
  onDrop : function (evt, transferData, session) {
    event.target.setAttribute("value",transferData.data);
  }
}

Le système de type utilisé permet à de multiples objets de types variés d'être glisser-déposer, et permet également à des formes alternatives de donnée de l'être. Le tableau suivant décrit quelques types de données que vous pourriez utiliser. Vous pouvez aussi définir votre propre type si nécessaire.

text/unicode Text data
text/html données HTML
application/x-moz-url un URL
application/x-moz-file Un fichier local

Dans la prochaine section, nous regarderons un exemple utilisant le glisser-déposer.