拖拽操作

这篇翻译不完整。请帮忙从英语翻译这篇文章

The following describes the steps that occur during a drag and drop operation.

该文档描述拖拽操作的过程。

属性 Draggable

在一个web页面中,有一些默认就可以被拖动的元素。它们包括选中的文本,images图片和链接。当一个图片或者链接被拖动时,这个被拖动的图片或者链接的URL就会先被设置为“拖动数据”(drag data),然后一次拖动过程就正式开始了。对于其他元素,若你要查看它们默认的拖动效果,它们必须是选中区域的一部分。查看默认的拖动效果:在一个web页面上选择一个区域,然后点击并按住鼠标,然后拖动选中区域。当拖动时,一个系统特殊渲染过的选择区将会显示,并且会跟随鼠标指针移动。当然,如果你没有设置监听事件调整被拖动的数据,上面这种拖动的行为只是普通的默认的拖动行为。

在HTML中,除了图片、超链接以及被选中区域,其它元素默认是不可拖拽的。所有的XUL元素也都是可以拖拽的。为了让别的HTML元素也能被拖拽,必须进行以下三步:

  • 为所需拖拽的元素设置属性 draggable属性。
  • 为 dragstart 事件添加侦听
  • 在侦听中设置拖拽数据。

这是一个允许一段内容被拖拽的实例:

<div draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'This text may be dragged')">
  This text <strong>may</strong> be dragged.
</div>

属性 draggable 被设置为 true,则元素变为可拖拽。如果该属性被忽略或者设置为 false,则元素不能被拖拽,文本可以被选中。该属性可被用于所有元素,包括图片和链接。然而,对于图片和链接,是默认可以拖拽的。故只需对默认属性值为false的元素设置该值。

注意:一旦元素为可拖拽,元素中的文本或其他元素就无法通过鼠标点击或拖拽来实现选中了。但,可通过Alt+鼠标选中文本。

对于XUL元素,则无需设置该属性值,它们默认都是可拖拽的。

<button label="Drag Me" ondragstart="event.dataTransfer.setData('text/plain', 'Drag Me Button');">

开始一次拖拽操作

在这个例子中,通过使用 ondragstart 属性来添加一个 dragstart 事件监听器。

<div draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'This text may be dragged')">
  This text <strong>may</strong> be dragged.
</div>

拖拽操作一开始,dragstart事件就会被触发。在这个例子中,dragstart事件监听器被添加到可拖拽元素本身之中,但你也可以在其祖先元素中进行监听,因为正如其它大部分事件一样,拖拽事件是会冒泡的。在该事件中,你可以指定拖拽数据,反馈图片,以及拖拽效果。一般,只有拖拽数据是必须的,其他都可以自适应的。

拖拽数据(Drag Data)

所有的拖拽事件都有一个属性——dataTransfer,它包含着拖拽数据。

拖拽发生时,数据需与拖拽关联,以标识谁在被拖拽。如,当拖拽文本框中的选中文本时,关联到拖拽的数据即是文本本身。类似,拖拽网页上的链接时,拖拽数据即是链接的URL。

拖拽数据包含两类信息,类型(type)或者格式(format)或者数据(data),和数据的值(data value)。

格式即是一个表示类型的字符串(如,对于文本数据来说,格式为 "text/plain"),数据值为文本字串。当拖拽开始时,你需要通过提供一个类型以及数据来为拖拽添加数据。拖拽过程中,在dragenter 和 dragover 事件侦听中,需根据数据类型检测是否支持 drop(放下)。如,接收超链接的 drop 目标会检测是否支持链接类型 text/uri-list。在drop事件中, 侦听者(即事件处理函数)会获取拖拽中的数据并将其插入到 drop 位置。

类型指的是 MIME-type ,与 string 类似,如 text/plain 或者 image/jpeg。也可以自定义类型。常用类型列表参见:Drag Types

一次拖拽可能会提供多种不同类型的数据,这样就可以提供更详细的数据了(通常是自定义类型的数据)。可以为drop目标体提供后备数据,如果这些目标体不支持更具体的类型的数据的话。通常情况下,至少要指定类型为文本数据,即 text/plain 类型。看看下面的例子。

使用 setData 方法在 dataTransfer 中设置数据。需要提供两个参数:数据类型和数据值。例如:

event.dataTransfer.setData("text/plain", "Text to drag");

在这个例子中,数据值是"Text to drag",它的格式是"text/plain"

可以提供多种格式的数据。通过多次调用 setData 方法增加不同的格式。格式顺序需从 具体 到 一般。

var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.mozilla.org");
dt.setData("text/plain", "http://www.mozilla.org");

在这里,数据被以三种类型添加。第一个类型“application/x-bookmark'”是一个自定义类型。一般来说别的应用是不支持这种类型的,但可以在同一个网站或应用内的拖拽中使用这样的自定义类型。与此同时,通过提供更为一般的数据类型,你也可以使这一拖拽被其它应用支持。 'application/x-bookmark'类型的数据你可以设置得具体一些,为了使其它应用支持而设置的较为一般的数据类型的数据可以简单些,比如仅仅设置一个URL或者一段文本。

注意:本例中"text/rui-list"和"text/plain"类型的数据内容都相同。这样的设定经常发生,但也不是非这么设定不可。

若你试图两次以同一格式设定数据,新数据会替代旧数据,数据的位置还是和旧的一样。

你可以使用 clearData 方法来移除一个数据。需要传入一个参数:待移除的数据的类型。

event.dataTransfer.clearData("text/uri-list");

 clearData 方法的参数是可选的。如果没有传入参数,绑定其上的所有数据都会被移除。若没有设定数据,或者数据全被移除了,那么拖拽不会发生。

 

设置拖动反馈图片

When a drag occurs, an translucent image is generated from the drag target (the element the dragstart event is fired at), and follows the mouse pointer during the drag. This image is created automatically so you do not need to create it yourself. However, you can use the  to specify a custom drag feedback image.

当一次拖拽发生时,会从被拖拽的目标(即dragstart事件被触发的元素)处产生一个半透明的图片(反馈图片),该图片在拖拽过程中会跟随鼠标指针移动。这个图片是自动生成的,因此你无需亲自设定它。但你可以使用 setDragImage 来自定义一个反馈图片

event.dataTransfer.setDragImage(image, xOffset, yOffset);

需要传入三个参数。一个是指向一个图片的引用,通常是一个图片元素对象,但也可以是一个canvas或其它任何元素对象。这个图片在页面上怎么显示,它作为拖拽的反馈图片就怎么显示。你可以可以使用不在该文档中的图片或者canvas。见下面的例子:

function dragWithCustomImage(event)
{
  var canvas = document.createElementNS("http://www.w3.org/1999/xhtml","html:canvas");
  canvas.width = canvas.height = 50;

  var ctx = canvas.getContext("2d");
  ctx.lineWidth = 4;
  ctx.moveTo(0, 0);
  ctx.lineTo(50, 50);
  ctx.moveTo(0, 50);
  ctx.lineTo(50, 0);
  ctx.stroke();

  var dt = event.dataTransfer;
  dt.setData('text/plain', 'Data to Drag');
  dt.setDragImage(canvas, 25, 25);
}

当绘制自定义的拖拽反馈图片时这个技术很有用。

setDragImage 的第二第三个参数是图片显示时,相对于鼠标指针的位置。在这个例子中,canvas宽50像素,高50像素,我们设定偏移量为25,25,这样一来图片就会出现在鼠标正中。

拖动效果

 

拖动时,有几个可以执行的操作。复制操作(copy )用于指示将正在拖动的数据从其目前的位置复制到要放置位置。移动(move )操作用以指示被拖动的数据将被移动[译者注:也就是说被拖动的数据将从当前位置移动到放置位置]。链接(link )操作用来指明将在源位置与投放位置之间建立某些形式的关联或连接。

 

通过在dragstart事件监听器中设置 effectAllowed 属性,你可以为拖动源指定这三种操作中的某一种。

event.dataTransfer.effectAllowed = "copy";

在这个例子中,只有复制(copy)被允许。你可以用下面的各种方式组合这些值:

none
禁止任何操作
copy
只进行复制操作
move
只进行移动操作
link
只进行连接操作
copyMove
只进行复制或移动操作
copyLink
只进行复制或连接操作
linkMove
只进行连接或移动操作
all
复制,移动,或者连接

注意:你必须从上面列出的值中选一个来用。如果不指定 effectAllowed属性,那么所有的操作都是允许的,就像指定了"all"一样。所以你没必要指定这个值,除非你真的想指定特定的操作。

在一次拖拽操作中,绑定在dragenter 事件以及dragover事件上的监听器能够检查effectAllowed属性来确认哪些操作是允许的。在这些事件中,可以设定一个相关的属性 dropEffect ,用以指定一种允许的操作。该属性的值可以是 none,copy,move或者link。不能是形如copyMove这样的复合值。

With the dragenter and dragover event, the dropEffect property is initialized to the effect that the user is requesting. The user can modify the desired effect by pressing modifier keys. Although the exact keys used vary by platform, typically the Shift and Control keys would be used to switch between copying, moving and linking. The mouse pointer will change to indicate which operation is desired, for instance, for a copy, the cursor might appear with a plus sign next to it.

You can modify both the effectAllowed and the dropEffect property during the dragenter or dragover events, if for example, a particular drop target only supports certain operations. Modifying the effectAllowed property lets you specify which set of operations are permitted at a certain drop target. For example, setting the effectAllowed property to copyMove allows a copy or move operation but prevents the user from performing a link operation.

You can modify the dropEffect property to override the user effect, and enforce a specific drop operation to occur. Note that this effect must be one listed within the effectAllowed property. Otherwise, it will be set to an alternate value that is allowed.

event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.dropEffect = "copy";

In this example, copy is the effect that is performed as the specified effect is included in the list of allowed effects.

You can use the value none for either property to indiciate that no drop is allowed at this location.

Specifying Drop Targets

A listener for the dragenter and dragover events are used to indicate valid drop targets, that is, places where dragged items may be dropped. Most areas of a web page or application are not valid places to drop data. Thus, the default handling for these events is to not allow a drop.

If you want to allow a drop, you must prevent the default handling by cancelling the event. You can do this either by returning false from an attribute-defined event listener, or by calling the event's event.preventDefault method. The latter may be more feasible in a function defined in a separate script.

<div ondragover="return false">
<div ondragover="event.preventDefault()">

Calling the preventDefault method during both a dragenter and dragover event will indicate that a drop is allowed at that location. However, you will commonly wish to call the preventDefault method only in certain situations, for example, only if a link is being dragged. To do this, call a function which checks a condition and only cancels the event when the condition is met. If the condition is not met, don't cancel the event, and a drop will not occur there if the user releases the mouse button.

It is most common to accept or reject a drop based on the type of drag data in the data transfer. For instance, allowing images or links or both. To do this, you can check the types of the dataTransfer object. The types are a list of the string types that were added when the drag began, in the order from most significant to least significant.

function doDragOver(event)
{
  var isLink = event.dataTransfer.types.contains("text/uri-list");
  if (isLink)
    event.preventDefault();
}

In this example, we use the contains method to check if the type text/uri-list is present in the list of types. If it is, we will cancel the event so that a drop may be allowed. If the drag data does not contain a link, the event will not be cancelled and a drop cannot occur at that location.

You may also wish to set either the effectAllowed, dropEffect property, or both at the same time, if you wish to be more specific about the type of operation that will performed. Naturally, changing either property will have no effect if you do not cancel the event as well.

Drop Feedback

There are several ways in which you can indicate to the user that a drop is allowed at a certain location. The mouse pointer will update as necessary depending on the value of the dropEffect property. Although the exact appearance depends on the user's platform, typically a plus sign icon will appear for a 'copy' for example, and a 'cannot drop here' icon will appear when a drop is not allowed. This mouse pointer feedback is sufficient in many cases.

However, you can also update the user interface with an insertion point or highlight as needed. For simple highlighting, you can use the -moz-drag-over CSS pseudoclass on a drop target.

.droparea:-moz-drag-over {
  border: 1px solid black;
}

In this example, the element with the class droparea will receieve a 1 pixel black border while it is a valid drop target, that is, if the event.preventDefault method was called during the dragenter event. Note that you must cancel the dragenter event for this pseudoclass to apply, as this state is not checked for the dragover event.

For more complex visual effects, you can also perform other operations during the dragenter event, for example, by inserting an element at the location where the drop will occur. For example, this might be an insertion marker or an element that represents the dragged element in its new location. To do this, you could create an image or separator element for example, and simply insert it into the document during the dragenter event.

The dragover event will fire at the element the mouse is pointing at. Naturally, you may need to move the insertion marker around a dragover event as well. You can use the event's clientX and clientY properties as with other mouse events to determine the location of the mouse pointer.

Finally, the dragleave event will fire at an element when the drag leaves the element. This is the time when you should remove any insertion markers or highlighting. You do not need to cancel this event. Any highlighting or other visual effects specified using the -moz-drag-over pseudoclass will be removed automatically. The dragleave event will always fire, even if the drag is cancelled, so you can always ensure that any insertion point cleanup can be done during this event.

Performing a Drop

When the user releases the mouse, the drag and drop operation ends. If the mouse was released over an element that is a valid drop target, that is, one that cancelled the last dragenter or dragover event, then the drop will be successful, and a drop event will fire at the target. Otherwise, the drag operation is cancelled and no drop event is fired.

During the drop event, you should retrieve that data that was dropped from the event and insert it at the drop location. You can use the dropEffect property to determine which drag operation was desired.

As with all drag related events, the event's dataTransfer property will hold the data that is being dragged. The getData method may be used to retrieve the data again.

function onDrop(event)
{
  var data = event.dataTransfer.getData("text/plain");
  event.target.textContent = data;
  event.preventDefault();
}

The getData method takes one argument, the type of data to retrieve. It will return the string value that was set when the setData was called at the beginning of the drag operation. An empty string will be returned if data of that type does not exist. Naturally though, you would likely know that the right type of data was available, as it was previously checked during a dragover event.

In the example here, once we have retrieved the data, we insert the string as the textual content of the target. This has the effect of inserting the dragged text where it was dropped, assuming that the drop target is an area of text such as a p or div element.

In a web page, you should call the preventDefault method of the event if you have accepted the drop so that the default browser handling does not handle the droppped data as well. For example, when a link is dragged to a web page, Firefox will open the link. By cancelling the event, this behaviour will be prevented.

You can retrieve other types of data as well. If the data is a link, it should have the type text/uri-list. You could then insert a link into the content.

function doDrop(event)
{
  var links = event.dataTransfer.getData("text/uri-list").split("\n");
  for each (var link in links) {
    if (link.indexOf("#") == 0)
      continue;

    var newlink = document.createElement("a");
    newlink.href = link;
    newlink.textContent = link;
    event.target.appendChild(newlink);
  }
  event.preventDefault();
}

This example inserts a link from the dragged data. As you might be able to guess from the name, the text/uri-list type actually may contain a list of URLs, each on a separate line. In this code, we use the split to split the string into lines, then iterate over the list of lines, inserting each as a link into the document. Note also that we skip links starting with a number sign (#) as these are comments.

For simple cases, you can use the special type URL to just retrieve the first valid URL in the list. For example:

var link = event.dataTransfer.getData("URL");

This eliminates the need to check for comments or iterate through lines yourself, however it is limited to only the first URL in the list.

The URL type is a special type used only as a shorthand, and it does not appear within the list of types specified in the types property.

Sometimes you may support a number of different formats, and you want to retrieve the data that is most specific that is supported. In this example, three formats are support by a drop target.

The following example returns the data associated with the best supported format:

function doDrop(event)
{
  var types = event.dataTransfer.types;
  var supportedTypes = ["application/x-moz-file", "text/uri-list", "text/plain"];
  types = supportedTypes.filter(function (value) types.contains(value));
  if (types.length)
    var data = event.dataTransfer.getData(types[0]);
  event.preventDefault();
}

This method relies on JavaScript functionality available in Firefox 3. However the code could be adjusted to support other platforms.

Finishing a Drag

Once the drag is complete, a dragend is fired at the source of the drag (the same element that received the dragstart event). This event will fire if the drag was successful or if it was cancelled. However, you can use the dropEffect to determine what drop operation occurred.

If the dropEffect property has the value none during a dragend, then the drag was cancelled. Otherwise, the effect specifies which operation was performed. The source can use this information after a move operation to remove the dragged item from the old location. The mozUserCancelled property will be set to true if the user cancelled the drag (by pressing Escape), and false if the drag was cancelled for other reasons such as an invalid drop target, or if it was successful.

A drop can occur inside the same window or over another application. The dragend event will always fire regardless. The event's screenX and screenY properties will be set to the screen coordinate where the drop occurred.

After the dragend event has finished propagating, the drag and drop operation is complete.

文档标签和贡献者

向此页面作出贡献: ylc395, wangxb, ziyunfei, hanyuxinting
最后编辑者: ylc395,