XUL Tutorial - 8.7 - Drag and Drop Example
Previous Contents Reference Next

XUL Tutorial - Drag and Drop Example

An example of implementing drag and drop will be implemented in this section.

Dragging Elements Around

Here, we'll create a simple board where items from a palette can be dragged onto the board. The user can click on one of several XUL elements on the palette and drag it onto a bulletin board to create an element of a particular type.

First, we'll add the wrapper scripts:

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

<script src="dragboard.js"/>

An additional script file dragboard.js is included which will contain the code we will write ourselves.

The board will be created using a bulletinboard element. We'll set some style properties to set the width and height of the bulletin board. A maximum size is also specified so that it doesn't resize when new elements are dragged onto it.

The board will need to respond to the dragdrop event so that an element is created when the user drags onto it.

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

The board only needs to respond to the dragdrop event. We could also add a handler for the dragover event, say to draw a highlight or update a status bar. We'll add a boardObserver in the file dragboard.js.

Next, a palette will be added to the right side of the window. It will contain three buttons, one to create new buttons, one for check boxes and the other text fields. This buttons will respond to the draggesture event and start a drag.

<box orient="vertical">

<button value="Button"
        elem="button" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button value="Check Box"
        elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button value="Text Field"
        elem="textfield" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
</box>

The nsDragAndDrop object will be called to do most of the work. We'll create a listObserver object that will set the data to be dragged. Note that each button here has an additional elem attribute. The is a made-up attribute. XUL doesn't handle it and just ignores it, but we can still retreive it with the DOM's GetAttribute function. We need this so that we know what type of element to create when dragging.

Next, we'll define the two listener objects. First, the listObserver which needs a function to handle the start of the drag.

var listObserver = { 
  onDragStart: function (evt){
    var txt=evt.target.getAttribute("elem");
    var flavours = { };
    flavours["text/unicode"] = { width: 2, data: txt };
    return flavours;
  },
};

One function has been defined, onDragStart, which will be called by the nsDragAndDrop object when necessary. The function should return a flavour list that contains the data indexed by type.

The elem attribute is retrieved from the target of the drag event. The target will be the element that had the drag start on. We'll use the value of this attribute as the data of the drag.

The boardObserver will need two functions, getSupportedFlavours and onDrop. The onDrop function will grab the data from the drag seesion and create a new element of the appropriate type.

var boardObserver = {
  getSupportedFlavours : function () {
    var flavours = { };
    flavours["text/unicode"] = { width: 2, iid: "nsISupportsWString" };
    return flavours;
  },
  onDrop: function (evt,dropdata){
    if (dropdata.data.data!=""){
      var elem=document.createElement(dropdata.data.data);
      evt.target.appendChild(elem);
      elem.setAttribute("left",""+evt.pageX);
      elem.setAttribute("top",""+evt.pageY); 
      elem.setAttribute("value",dropdata.data.data);
    }
  }
};

The getSupportedFlavours function needs only to return a list of flavours that the bulletin board can accept to be dropped on it.

The onDrop handler first creates a new element of the type stored in the drag session. Next, appendChild is called to add the new element to the bulletin board, which is the target of the event. Finally, we set some attributes on the new element.

The position of elements in a bulletin board is determined by the left and top attributes. The values of the pageX and pageY properties store the mouse pointer coordinates on the window where the drop occured. This allows us to place the new element at the position where the mouse button was released. This isn't quite the correct way to do this as we actually need to calculate the coordinates of the event relative to the bulletin board. It works here because the board is at the top-left corner.

The value attribute is set to data from the drag also so that the button has a default label.

This example is fairly simple. One possible change is to use a custom type for the data instead of text. The problem with using text is that if the text from an external drag just happens to be set to 'button', a button will be created on the board. A custom type means that the board will only accept drags from the palette (and anywhere else that type is dragged from).

The final code is shown below:

Example 8.7.1
<window title="Widget Dragger" id="test-window"
   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="chrome://global/content/nsJSSupportsUtils.js"/>
<script src="chrome://global/content/nsJSComponentManager.js"/>
<script src="dragboard.js"/>

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

<box orient="vertical">

<button value="Button"
        elem="button" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button value="Check Box"
        elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
<button value="Text Field"
        elem="textfield" ondraggesture="nsDragAndDrop.startDrag(event,listObserver)"/>
</box>

</window>
Example 8.7.2
var listObserver = { 
  onDragStart: function (evt){
    var txt=evt.target.getAttribute("elem");
    var flavours = { };
    flavours["text/unicode"] = { width: 2, data: txt };
    return flavours;
  },
};

var boardObserver = {
  getSupportedFlavours : function () {
    var flavours = { };
    flavours["text/unicode"] = { width: 2, iid: "nsISupportsWString" };
    return flavours;
  },
  onDrop: function (evt,dropdata){
    if (dropdata.data.data!=""){
      var elem=document.createElement(dropdata.data.data);
      evt.target.appendChild(elem);
      elem.setAttribute("left",""+evt.pageX);
      elem.setAttribute("top",""+evt.pageY); 
      elem.setAttribute("value",dropdata.data.data);
    }
  }
};

(Next) Next, we'll look at using style sheets with XUL files.

Examples: 8.7.1 8.7.2

XUL Tutorial - 8.7 - Drag and Drop Example
Previous Contents Reference Next