Haoran Blog  -  Stay hungry  Stay foolish

jQuery插件-Drag and Drop

今天要做将之前的Jquery实现Drag and Drop 功能. 查了一下相关资料 发现一个很好的网页,转一下链接。

http://jqueryui.com/draggable/#constrain-movement

这里有最简单的drop and drop功能。

接着想实现drag and drop到固定区域触发事件,然后找到了一个很详细的讲解。转发一下

Drag 和 Drop 在 JavaScript 中的使用

 

这一节描述了我们怎么在 JavaScript 中使用 drag and drop.

The JavaScript Drag and Drop Wrapper

在进行拖拽的时候我们也许会需要处理一些事件和传递数据,那么这个时候我们就需要用到Drag和Drop了。让我们简明的看一下使用方法。(对照文档有简化)

注意你需要在所用的xul页面中导入两个 chrome 路径。

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

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

我们可以参考下这个例子:

<button label=”Drag Me” ondraggesture=”nsDragAndDrop.startDrag(event,buttonObserver);”   />

它的功能是在我们首次拖动这个按钮时会通过触发nsDragAndDrop.startDrag 而执行buttonObserver方法里的onDragStart事件。第一个参数是所触发的事件元素,适用于所有的事件处理。第二个参数是作为监听器的方法,一会儿再具体介绍。 同样我们也可以处理其他的情况而触发相应的方法,例如下面这个例子:

<description value=”Click and drag this text.”

             ondraggesture=”nsDragAndDrop.startDrag(event,textObserver)”

             ondragover=”nsDragAndDrop.dragOver(event,textObserver)”

             ondragexit=”nsDragAndDrop.dragExit(event,textObserver)”

             ondragdrop=”nsDragAndDrop.drop(event,textObserver)”   />

通常有六个拖动触发事件,拖动是指按住鼠标并移动鼠标的时候,ondrag为有拖动操作就触发,ondraggesture为首次拖动而触发,ondragover为在区域内拖动而触发,ondragenter为拖动至区域内而触发,ondragexit为拖动至区域外而触发,ondragdrop为释放拖动而触发。

这有两种方式处理drag 和 drop 事件。第一个是直接用nsIDragService和 nsIDragSessionXPCOM接口,第二个是用JavaScript wrapper 方法协助操作。

如上所述,在触发事件时候并没有什么需要特别注意的,一会让我们亲自来测试一下吧。先看一下这些事件的功能。

onDragStart(event, transferData, action) 

在拖动开始时你可以定义这个方法,它有三个参数,第一个参数event元素作为被拖动的节点事件,我们可以用event.target取得该DOM节点;第二个transferData是拖拽时候所转移的数据,我们可以在这个方法内给transferData添加数据而进行传递,具体的使用方法请查看nsITransfer接口(尚未确定);第三个是拖拽动作的类型,有四种类型供用户选择,但具体意义还不明确。

nsIDragSession.DRAGDROP_ACTION_NONE 不允许修改。

nsIDragSession.DRAGDROP_ACTION_COPY 拷贝

nsIDragSession.DRAGDROP_ACTION_MOVE 移动

nsIDragSession.DRAGDROP_ACTION_LINK 连接

onDragOver(event, flavour, session) 

当拖动在区域内时你可以定义这个方法,第一个参数还是event元素;第二个参数是所此次拖动所带来的传递数据类型相关,里面应该包括着数据;第三个session是会话信息,它有几个参数我个人认为很有用,比如sourceDocument可以得到被拖动的源,sourceNode可以得到被拖动的节点等等,你可以从中直接取到节点属性。

onDragExit(event, session) 

当拖动退出区域时你可以定义这个方法,第一个参数又是event元素,第二个是会话信息解释同上。

onDrop(event, transferData, session) 

当释放拖动后你可以定义这个方法来做一些处理,第一个依然是event;第二个是传递过来的数据;第三个session是会话信息解释同上。

getSupportedFlavours() 

这个方法可以返回可接受的元素类型,通常用在Drop事件中,具体使用方法请参照例子,但好像就是一个数据类型的判定,还需要研究下。

The type of data being dragged is stored as a set of flavours. Often, a dragged object will be available in a number of flavours. That way, a drop target can accept the flavour it finds most suitable. For example, a file may come in two flavours, the file itself and the text name of the file. If the file is dragged and dropped onto a directory, the file flavour will be used. If the file is dropped onto a textbox, the text name flavour will be used. The text is therefore used to insert the name of the file when files can’t be dropped directly.

A flavour object has a name, which is a formatted like a MIME type, such as ‘text/unicode’. Within the onDragStart function, you specify what flavours are available for the item being dragged. To do this, add data and flavours to the transferDataobject, which is the second argument to onDragStart.

这是mozilla技术文档的例子,在 onDragStart 方法里给transferData 元素添加数据。

var textObserver = {

  onDragStart: function (event, 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);

  }

}

Here, an observer has been declared and stored in the variable textObserver. It has one property called onDragStart. (In JavaScript, properties can be declared with the syntax name : value). This property is a function which sets the data that is being dragged.

一旦被触发,它会拖拽的数据是“Cabbage”。当然,如果你想得到的是被点击过的元素属性,用我上面说的方法,以event.target取得该DOM节点。

我们在拖拽的时候创建一个传递数据流TransferData 元素,我们在其中添加了两个不同类型的数据,第一个是HTML类型的字符串,第二个是plain类型的字符串。如果用户拖拽的页面能够接受HTML元素的话,那页面会接受HTML类型的元素,文本就会变成粗体。否则会用plain类型的文本来进行替换。

通常你会想提供能够让更多程序接受拖拽的数据,因而你定义的flavours 的次序决定了匹配的优先级。就像上面的情况一样HTML flavour (text/html)优先于text flavour (text/unicode)。

这个例子演示了当拖拽时将节点的label标签的值作为数据而进行传递。

var textObserver = {

  onDragStart: function (event) {

    var txt = event.target.getAttribute(“label”);

    transferData.data = new TransferData();

    transferData.data.addDataForFlavour(“text/unicode”,txt);

  }

}

这个在单元树上进行拖拽是非常有效果的,你可以在某个单元树取值,如果是一个模板建造树那这个值可能会来自RDF文件。假设你储存它是一个字符串,只要是字符串元素的拖拽,它就可以获取到释放过来的数据啦。

如果你想发送更多的数据(例如多选文件),你必须用 TransferDataSet ,如下所写:(但貌似我没测试成功。—chenjian)

var textObserver = {

  onDragStart: function (event) {

    var txt1 = ‘hello’;

    var txt2 = ‘there’;

    transferData.data = new TransferDataSet();

    var tmp = new TransferData();

    tmp.addDataForFlavour(“text/unicode”,txt1);

    transferData.data.push(tmp);

    new TransferData();

    tmp.addDataForFlavour(“text/unicode”,txt2);

    transferData.data.push(tmp);

  }

}

//你需要开始拖拽或是接收拖拽元素时候为每一个元素添加监听

You will need to add an observer to each element that can either start a drag action or can accept dropped objects. You can reuse the same observer for multiple elements. For an element that can start a drag, onStartDrag is all that is necessary to implement.

监听器需要实现getSupportedFlavours ,onDragOver  和onDrop这三个方法,那些元素才能够正常拖拽和接受释放,对于这种情况在拖拽元素的界面onStartDrag 方法也是有必要的。

getSupportedFlavours 方法可以传回flavours 的列表,才能够知道哪些类型的元素可以在其区域内进行释放。比如一个文件系统目录显示可以接收文件和文本类型,但他不能接收HTML text。下面的例子我们定义getSupportedFlavours 方法,我们只允许字符串类型的 flavour 。

var textObserver = {

  getSupportedFlavours : function () {

    var flavours = new FlavourSet();

    flavours.appendFlavour(“text/unicode”);

    return flavours;

  }

}

在 flavours list 只有一个flavour ‘text/unicode’。在FlavourSet 元素中可以加入flavours。在某些情况下你必须提供XPCOM接口,如下个加文件的例子:

var textObserver = {

  getSupportedFlavours : function () {

    var flavours = new FlavourSet();

    flavours.appendFlavour(“application/x-moz-file”,”nsIFile”);

    flavours.appendFlavour(“text/unicode”);

    return flavours;

  }

}

在元素拖拽到区域之内会触发onDragOver 方法。You might use it to change the appearance of the element as it is being dragged over. In many cases the function can do nothing. It must be defined for elements that accept dragged data however.

之后,会触发onDrop 方法。第二个参数transferData是被传进来的数据流,与此同时,某wrapper会查看 getSupportedFlavours 来判断最佳类型flavour, 所以transferData传进来的数据只包含最佳类型选择的那个flavour。

The transfer object has two properties, data which holds the data and flavour which holds the flavour of the data. Once you have the data, you can add it to the element is some way. For example, you might set the value of a textbox.

var textObserver = {

  onDrop : function (event, transferData, session) {

    event.target.setAttribute(“value”,transferData.data);

  }

}

The flavour system used allows multiple objects of various types to be dragged at once and also allows alternative forms of the data to be dragged. The following table describes some of the flavours you might use. You can also make up your own flavours if necessary.

text/unicode

Text data

text/html

HTML data

text/x-moz-url

A URL

application/x-moz-file

A local file

下面的这些还有待研究。而且是允许自行设置Flavor的。

Flavor

Brief Description

text/unicode

没有格式信息的两字节unicode编码版本,可以发送常用的字符。Two-byte unicode plain text with no formatting information

text/xif

An XML interchange format that includes text and its formatting similar to html

text/html

HTML

text/plain

One-byte plain text, for internal use only.

AOLMAIL

A format used by AOL/AIM for formatted text, similar to HTML

image/png

Binary PNG image data

image/jpg

Binary JPEG image data

image/gif

Binary GIF image data

application/x-moz-file

本地文件。 A file object (supporting nsIFile) for identifying a local file

text/x-moz-url

A url and title for identifying the data is a URL for platforms that care

下面转发一个找到的相关的实例

HTML5 drag & drop 拖拽与拖放简介

by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=1419

一、前面的话

HTML5提供专门的拖拽与拖放的API,以后实现这类效果就不必乱折腾了。但是,考虑到Opera浏览器似乎对此不感冒,在通用性上有待商榷,所以这里也就简单说一说。

二、相关重点

  1. DataTransfer 对象:退拽对象用来传递的媒介,使用一般为Event.dataTransfer。
  2. draggable 属性:就是标签元素要设置draggable=true,否则不会有效果,例如:
    <div title="拖拽我" draggable="true">列表1</div>
  3. ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
  4. ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
  5. ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
  6. ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
  7. ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上
  8. Event.preventDefault() 方法:阻止默认的些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。
  9. Event.effectAllowed 属性:就是拖拽的效果。

三、相关实例

为了便于理解上面的粗体的事件啊对象啊什么的,做了个很简单的demo页面。

您可以狠狠地点击这里:HTML5 drag & drop删除元素demo

此demo实现的效果是把右侧的列表拖动到左侧的写着果敢的“垃圾箱”三个字的div层上,此列表元素就会从这个星球上消失。如下截图连环画所示:
列表元素拖拽删除截图之鼠标经过 张鑫旭-鑫空间-鑫生活
html5 drag & drop ondragstart截图 张鑫旭-鑫空间-鑫生活
html5 drag & drop 拖拽与拖放经过目标元素截图 张鑫旭-鑫空间-鑫生活
HTML5 drag drop demo ondrop截图 张鑫旭-鑫空间-鑫生活

源代码展示
其中HTML结构如下:

<div><br />垃<br />圾<br />箱</div>
<div>
    <div title="拖拽我" draggable="true">列表1</div>
    <div title="拖拽我" draggable="true">列表2</div>
    <div title="拖拽我" draggable="true">列表3</div>
    <div title="拖拽我" draggable="true">列表4</div>
    <div title="拖拽我" draggable="true">列表5</div>
    <div title="拖拽我" draggable="true">列表6</div>
</div>
<div></div>

JS代码如下:

var $ = function(selector) {
    /*简单的选择器方法*/
    ...
};

var eleDustbin = $(".dustbin")[0], eleDrags = $(".draglist"), lDrags = eleDrags.length, eleRemind = $(".dragremind")[0], eleDrag = null;
for (var i=0; i<lDrags; i+=1) {
    eleDrags[i].onselectstart = function() {
        return false;
    };
    eleDrags[i].ondragstart = function(ev) {
        /*拖拽开始*/
        //拖拽效果
        ev.dataTransfer.effectAllowed = "move";
        ev.dataTransfer.setData("text", ev.target.innerHTML);
        ev.dataTransfer.setDragImage(ev.target, 0, 0);
        eleDrag = ev.target;
        return true;
    };
    eleDrags[i].ondragend = function(ev) {
        /*拖拽结束*/
        ev.dataTransfer.clearData("text");
        eleDrag = null;
        return false
    };
}
eleDustbin.ondragover = function(ev) {
    /*拖拽元素在目标元素头上移动的时候*/
    ev.preventDefault();
    return true;
};

eleDustbin.ondragenter = function(ev) {
    /*拖拽元素进入目标元素头上的时候*/
    this.style.color = "#ffffff";
    return true;
};
eleDustbin.ondrop = function(ev) {
    /*拖拽元素进入目标元素头上,同时鼠标松开的时候*/
    if (eleDrag) {
        eleRemind.innerHTML = '<strong>"' + eleDrag.innerHTML + '"</strong>被扔进了垃圾箱';
        eleDrag.parentNode.removeChild(eleDrag);
    }
    this.style.color = "#000000";
    return false;
};

四、匆匆结语

今天是年后第一天上班,没有多少想折腾的心情,所以,一些啰哩吧嗦的话就省了。虽然是HTML5的东西,但是,根据findmebyip中显示的浏览器支持情况来看,IE6~IE8都是有drag & drop API的(见下截图)。
IE6~IE8也是有drag&drop API的 张鑫旭-鑫空间-鑫生活

根据自己的简单的测试,低版本的IE浏览器确实支持诸如ondragstart事件,但是会报不认识dataTransfer的错误。可见IE在细节的处理上与现代浏览器有些不同。但是,目前自己没有这么多精气神把IE下的拖拽也折腾出来,故请原谅目前demo在IE下是纹丝不动,没有效果也不报错的。回头有功夫,一定会把IE相关的些东西补上。

发表评论

电子邮件地址不会被公开。 必填项已用*标注