拖拽实例
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 stack
element to create an element of a particular type.
创建一个面板,用于拖拽目的地。把调色板元素拖拽到 stack 元素上创建一个特殊类型的元素值。
First, we'll add the wrapper scripts:
首先,我们需要添加封装脚本。
<script src="chrome://global/content/nsDragAndDrop.js"/> <script src="chrome://global/content/nsTransferable.js"/> <script src="dragboard.js"/>
An additional script file dragboard.js
is included which will contain the code we will write ourselves.
我们需要自己编写 dragboard.js 文件。
The board will be created using a stack
element. We'll use some style properties to set the width and height of the stack. A maximum size is also specified so that it doesn't resize when new elements are dragged onto it.
使用 stack 创建一个面板。使用CSS样式设置 stack 的宽和高。需要设置一个最大值。这样拖拽进来的元素不会改变其大小。
The board will need to respond to the dragdrop
event so that an element is created when the user drags onto it.
面板需要对 dragdrop 事件进行响应,这样,拖拽到面板时,才会创建相应元素。
<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>
The board only needs to respond to the dragdrop
and dragover
events. We'll add a boardObserver
to the file dragboard.js
in a moment.
该面板只需要响应 dragdrop 和 dragover 事件。我们需要在 dragboard.js 文件中添加 boardObserver 对象。
Next, a palette will be added to the right side of the window. It will contain three buttons, one to create new buttons, one to create check boxes and the other to create textboxes. This buttons will respond to the draggesture
event and start a drag.
接着,调色板需要添加到窗口的右侧位置。它包含三个按钮,一个用于创建新按钮,一个用于创建复选框,另一个用于创建文本框。这些按钮将响应 draggesture 事件,并开始拖拽。
<vbox> <button label="Button" elem="button" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> <button label="Check Box" elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> <button label="Text Box" elem="textbox" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> </vbox>
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. This is a made-up attribute. XUL doesn't handle it and just ignores it, but we can still retrieve it with the DOM's getAttribute function. We need this so that we know what type of element to create when dragging.
listObserver 对象设置被拖拽的数据,每个按钮都有一个附件属性 elem,为自定义属性。XUL不处理它,只是忽略它,但是我们可以通过 getAttirbute 方法检索到它。通过它我们可以确知在拖拽的时候创建什么类型的元素。
Next, we'll define the two listener objects. First, the listObserver
which needs a function to handle the start of the drag.
接着,我们定义两个侦听对象。首先, listObserver 需要定义一个函数处理开始拖拽行为。
var listObserver = { onDragStart: function (event, transferData, action) { var txt = event.target.getAttribute("elem"); transferData.data = new TransferData(); transferData.data.addDataForFlavour("text/unicode", txt); } }
One function has been defined, onDragStart
, which will be called by the nsDragAndDrop
object when necessary. The function adds the data to be dragged to the transfer object. 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.
定义了一个函数 onDragStart,需要时, nsDragAndDrop 会调用该方法。该方法向 transfer 对象添加了用于拖拽的数据。elem 属性可以从拖拽事件的目标体中获取。目标体即是拖拽行为的附属体。我们将使用该属性值作为我们拖拽的数据。
The boardObserver
will need three functions, getSupportedFlavours
, onDragOver
and onDrop
. The onDrop
function will grab the data from the drag session and create a new element of the appropriate type.
boardObserver 需要三个函数,getSupportedFlavours,onDragOver, onDrop。其中 onDrop 方法将从拖拽回会话中获取数据,并创建一个合适类型的元素。
var boardObserver = { getSupportedFlavours : function () { var flavours = new FlavourSet(); flavours.appendFlavour("text/unicode"); return flavours; }, onDragOver: function (event, flavour, session) {}, onDrop: function (event, dropdata, session) { if (dropdata.data != "") { var elem = document.createElement(dropdata.data); event.target.appendChild(elem); elem.setAttribute("left", "" + event.pageX); elem.setAttribute("top", "" + event.pageY); elem.setAttribute("label", dropdata.data); } } }
The getSupportedFlavours
function needs only to return a list of flavours that the stack
can accept to be dropped on it. In this case, it only accepts text. We don't need to do anything special for the onDragOver
function, so no code is added in its body.
getSupportedFlavours 方法只需返回一系列的数据类型,这些数据类型即是 stack 可以接收的拖拽数据的数据类型。上述例子中,只能接收 text。我们不需要在拖拽过程中做特殊处理,所以 onDragOver 方法为空。
The onDrop
handler first uses the createElement
function to create a new element of the type stored in the drag session. Next, appendChild
is called to add the new element to the stack
, which is the target of the event. Finally, we set some attributes on the new element.
onDrop 方法初次使用 createElement 方法创建拖拽会话中存储类型的元素。接下来, appendChild 方法向 stack(即事件的目标体)中添加了一个新元素。最后,我们对该元素设置了一些属性。
The position of elements in a stack
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 stack
. It works here because the board is at the top-left corner of the window.
stack 中元素的位置取决于其 left 和 top属性。pageX 和 pageY 属性存储了拖拽时鼠标指针在窗口上的坐标值。
The label
attribute is set to the 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 flavour 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.
The final code is shown below:
<window title="Widget Dragger" id="test-window" orient="horizontal" xmlns="https://www.mozilla.org/keymaster/gat...re.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="Button" elem="button" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> <button label="Check Box" elem="checkbox" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> <button label="Text Box" elem="textbox" ondraggesture="nsDragAndDrop.startDrag(event, listObserver)"/> </vbox> </window>
var listObserver = { onDragStart: function (event, transferData, action) { var txt = event.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 (event, flavour, session) {}, onDrop: function (event, dropdata, session) { if (dropdata.data != "") { var elem = document.createElement(dropdata.data); event.target.appendChild(elem); elem.setAttribute("left", "" + event.pageX); elem.setAttribute("top", "" + event.pageY); elem.setAttribute("label", dropdata.data); } } };
Original Document Information
- Author(s): Neil Deakin
- Original Document: https://xulplanet.com/tutorials/mozsdk/dragex.php
- Copyright Information: Copyright (C) Neil Deakin