使用HTML5本地 Drag和Drop API(native API)

人人都爱使用方便、.具备互动的用户界面。而且随着智能手机的引入,用户的指望瞬间高了一大截。他们但愿网站一目了然,交互动做尽人皆知。总之,他们但愿你的网站使用起来超级简单。javascript

让你的用户能在你的网站实现拖拽之类的操做吧,这会让你的网站更加具备交互性。由于人们都知道如何把东西从X拖到Y位置,知道若是把A拖到B前面的话,那么A就会先显示出来。php

处理拖动,拖入之类的操做之前老是javascript的事情,开发者们早先也有本身的方法构建交互动做或者使用预制解决方案。随着HTML5 Drag和Drop API的出现,开发者们可使用本地事件和属性来处理这些交互动做了。html

简要介绍

那么就让咱们浏览一下这些API好让咱们对它的工做方式有个大体的了解吧。html5

native API让咱们能够经过使用draggable="true"属性来让想要的元素能够拖动。一些元素甚至不须要作任何修改,已是默承认拖拽的了(好比文本和图片)。java

默认的, 若是可拖动元素被拖动,只有form元素,好比input能够接手这些元素做为拖入。你或许早先见过这个:若是你选中一些文字而且将它们拖进一个textarea,文本就会被复制到textarea。jquery

native API也被用来处理从位于OS上的外部区域拖动到drop区域的操做。几乎全部优秀的内容管理系统都提供了拖出和拖入上传内容功能。由于这些元素都是外部的,因此你只须要搞清楚拖入区域(固然还须要一个可兼容的浏览器)。浏览器

一笔带过移动端

目前native API还不支持移动端。这在从此可能会有所改变,最好是在PC上看演示实例,这样你才能够明白它都是怎么工做的。app

Drop和Drag API 事件(Events)

native API提供下列可帧听event.。这些event会在可拖拽元素或可拖放区域被应用,在设置的同时触发。jsp

当这些event被成功触发,咱们就开始使用一个本地对象(咱们称之为event)。这个对象拥有的关于event的信息比event自身还要多,同时这个对象也让你可以使用datatransfer对象。咱们大部分方法和属性都须要经过后者来设置。ide

咱们须要给每一个event都设置一个回调,这样才能和这些API进行交互:

  1. // add a handler to trigger on dragstart
  2. document.addEventListener('dragstart', function(event) {
  3. // add your dragstart code here
  4. }, false);
 

 

 拖拽相关event

这些event只有在可拖拽元素里才会被触发

dragstart

一旦咱们开始拖拽某一个元素,这个event就被触发。在这里咱们须要告诉API咱们将拖拽的元素和一些别的参数。使用setData()方法来设置须要保存的数据,为可拖拽元素设置effectAllowed()属性,使用setDragImage()来定义可拖拽助手。

drag

这个event在拖拽过程当中被持续触发。发生的次数取决于浏览器。这个被用来监测被拖拽元素的位置。

dragend 

这个event在可拖拽元素被放置好后触发(不考虑放置位置),并且通常会在放置区域的drop event以后触发。在拖动的时候,你可使用这个event来重设样式或者别的页面展现动做。dragend event可使用被拖拽元素,因此能够在拖拽完毕后进行一些计算(好比经过查看新添加元素来看看drop evnet是否触发,而后移除初始的元素。)

拖放相关event

这些event只有在被定义为拖放目标的元素中才会被触发。(或者默认是拖放目标的元素,好比form元素)

dragenter

只在可拖拽元素进入拖放区域时被触发一次。这个event会在50%可拖拽元素进入拖放区域的时候被触发。

这个event设置拖放区域的dropEffect()。拖放至非form元素默认不采起任何操做。你须要手动设置event.preventDefault()和event.stopPropagation()来告诉API本次拖放操做能够执行。

你能够将拖拽元素设置的dataTransfer对象的effectAllowed值与拖放区域的dropEffect的值进行比较。若是这些值不能一直协同工做(好比一个是copy,另外一个是link),那么浏览器就不会放置该元素(即便你手动设置过了preventDefault和stopPropagation)。

你可使用types属性来获取dragstart中设置的全部数据类型。你没法看到具体数据可是你能够看到数据是什么类型。同时可使用contains方法来查看某一种特定类型数据是否被设置。这个是经过event.dataTransfer.typs.contatins(type)方法完成的。好比你能够用这种方式来看看text/html中的某样东西是否设置过了。

能够用过设置一些类或触发动做来让用户知道可拖动元素已经被成功放入拖放区域(一般是改变拖放区域的样式来证实操做已完成)。

dargover

这个event和dragenter基本同样,可是会在可拖放元素进入拖放区域后持续触发。若是你须要知道被拖动元素的准确位置,那么这个event再适合不过了(由于它的数值不断更新)。

dragleave

这个evnet在可拖拽元素离开拖放区域的时候被触发。它一般被用来移除dragenter或者dragover的样式。只会在可拖动元素与拖放区域再也不有重合部分时被触发一次。

drop

这个event在可拖动元素被松开同时拖放区域可接受此元素时被触发。这个只有在可拖动元素和拖放区域有正确的dropeffect和effectAllowed值时才会被触发。在放置时须要经过getdata方法获取有关信息。

Drag 和 Drop API 方法

在处理本地drag和drop API时,咱们主要是和dataTransfer对象打交道。它做为回调函数的一部分呈如今咱们面前同时也提供了几个别的可用函数。

setData

这个方法经过调用event.dataTransfer.setData(type.data)方法来设定将会从可拖动元素搜集的数据。你须要给其传递将被搜集的数据类型(type)以及数据自己。必须在dargstart中设置不然会不起做用。它的值只会在drop event以后被采集。

type最好是applicable data type。可是假如你使用的是Chorme、Safari、Firefox,那么你可使用像text/html、text/uri-list这些类型。若是你用的是IE,那么你须要将其设置为Text或者URL(必须彻底一致不然会出错)。

同时建议data是你想保存的数据。你能够保存一个URL,一大块HTML或者任何别的数据。每个type中只能设置一份data。好比你在text/html中保存的是一些HTML,那么你不能再经过setData()方法来保存一个新的data。由于这样会使旧data被覆盖。

getData

它和setData()相对应,是用来在startdrag event期间收集被拖动元素数据的。经过调用event.dataTransfer.getData(type)来采集须要的指定类型信息。

你会常常用到event.dataTransfer.types来查看所设置的类型以弄清楚传递的数据形式。若是你试图使用一种未被设置过的类型,IE将会报错。

这个方法只有在drop event中API提供了值以后才可使用,这样才能够采集数据。(这是为了在传输过程当中保护数据)

clearData

正如名字,它使用event.dataTransfer.clearData(type)来清除使用setData设置的数据。你须要制定将被删除的数据类型(好比:text/html或者url)。这个方法只能在dragstart event中使用。

setDragImage

这个方法使用event.dataTransfer.setDataImage()来设置被拖动图片拖动过程当中显示的样子。默认显示为半透明的状态。你可使用这个方法设置成本身须要的样子。这个方法在除IE以外的浏览器中均可以运行,并且目前也没有让IE支持的打算。

Drag 和 Drop API属性

 下面是一些在dataTransfer对象中能够设置的属性。咱们使用从event回调中传递的event变量来设置这些属性。

effectAllowed

这是可拖动元素特有的属性。它将告诉API drag event相关信息以及光标的展现形式(取决于OS和浏览器)。它经过给dragStart event中的evnet.dataTransfer.effectAllow赋值进行调用同时从copy,move,link,copyLink,copyMove,linkMove,all,none或者uninitialized获取possible value。

若是该值和dropEffect不匹配,那么它将阻止调用drop event(这样确保了拖放的准确性)。

dropEffect

这个属性制定了拖放区域以及可被拖放区域接受的拖放元素。它应该在dragenter或dragover期间经过event.dataTranser.dropEffecr被赋值。dropEffect从copy.link,move或none中获取possible value.

和effectAllow同样,若是值和effectAllow不匹配,那么它将阻止调用effectAllow event(这样确保了拖放的准确性).

files

这个属性包含了一个全部被设置过的本地文件列表。经过event.dataTransfer.files调用。只有被请求的文件才能从系统拖动到页面中(好比将桌面上的图片拖动到网页的上传栏中)。这个属性在网页上的元素被拖动时将是空的。(好比你拖动一张图片,那么不会为files设置任何数据)。

这步也能够检查咱们是否拥有文件,若是有,那么能够经过使用fileReader 对象来读取以及处理文件内容。

effectAllow 和 dropEffect实战

若是你想了解这些属性的具体用法,那么应该看看下面的CodePen demo(我尽可能作个demo放文章中,可是不必定有时间,谁作了私信我)。

点我看demo.

咱们定义了多个不一样的可拖拽项目同时设置它们能够被放置的区域,咱们也建立了几个拖放区域并设置了这些区域能够接收的项目类型。正确的设置这些属性可让浏览器明白哪些可拖拽元素能被放置进来。

虽然IE支持effectAllowed和dropEffect,可是它自身没法实现只容许合适的元素被拖放到拖放区这个功能。Chrome,Safari,Firefox会限制被拖动的项目同时阻止不合适的拖放,拒绝触发drop event。在IE中你可能须要经过在drop event触发时比较一些值来人工判断是够拒绝拖放。

用native API构建一些东西

这些API处理的信息太多了,所以咱们将全部东西在下面的练习中结合起来。

native API主要考虑的是dragable和dropable元素之间的交互以及其中的数据传递。native API不关心你正在移动两个元素从而交换它们的位置。它跟在意的是它们的数据。而这让native API不同凡响。

最有用的是native API能够处理多种数据类型和从多个位置获取的数据。

数据类型包括:

  • 普通字符串
  • Text/HTML 内容
  • URL列表
  • 单个或多个文件
  • 多种其它类型/自定义类型

数据来源包括:

  • 来自被拖动或放置的元素
  • 来自另外一个标签页,窗口,浏览器的可拖放元素
  • 来自本地源(好比你的电脑)

处理Drag和Drop之间的数据

native API提供了对可拖放元素的基础支持。API让你能够经过event知道drag是何时发生的,不想JQurey UI,n你须要手工移动/复制元素来校准API。

这是由于当你移动一个元素的时候,你的触发器是设置dragStart event,你已经给它设置好了想要传输的数据(还包括但愿可拖动元素拥有的一些效果,好比复制,移动等等)。当最终放置好可拖动元素时,好比放到了正确的位置,它就会触发拖放区域的drop event.它承载的是你想移动的数据而不是UI元素(UI元素须要用JavaScript手动调整)。

让咱们看一个例子,这样你就能清楚的知道它是怎么工做的。

例:一个可拖放拼图游戏

经过这个例子了解咱们是如何利用API在同一个页面的两个不一样元素之间传递数据的。

点我看例子。(谁作了这个demo告诉我)

本例中咱们定义了一些区域,左侧存放的是咱们的一些碎片,而右侧是一些空的可放置区域。这个游戏就是将左边的碎片拖到右边,完成这个拼图。

设置dragStart数据

在本游戏的dragStart event中,咱们先是设置了effectAllow来告诉元素它接受一个copy类的拖动。

而后咱们搜集scr(图片源)和outerHTML(HTML节点)并将它们放入咱们的数据传输对象中,好比text/url-list和text/html。若是是IE,咱们就只保存被拖动元素的src并保存为它的text格式。

  1. var dragItem;
  2. // triggered draggable as we start dragging
  3. function dragStart(event) {
  4. drag = event.target;
  5. dragItem = event.target;
  6.  
  7. // set the effectAllowed for the drag item
  8. event.dataTransfer.effectAllowed = 'copy';
  9.  
  10. var imageSrc = $(dragItem).prop('src');
  11. var imageHTML = $(dragItem).prop('outerHTML');
  12.  
  13. // check for IE (it supports only 'text' or 'URL')
  14. try {
  15. event.dataTransfer.setData('text/uri-list', imageSrc);
  16. event.dataTransfer.setData('text/html', imageHTML);
  17. } catch (e) {
  18. event.dataTransfer.setData('text', imageSrc);
  19. }
  20.  
  21. $(drag).addClass('drag-active');
  22. }
 

 正确的effcetAllow/dropEffect

在咱们放置拼图碎片以前,dargEnter和dragOver就已经被触发了。记住,为了能放置元素,咱们须要经过返回false/prevent default来告诉浏览器这个元素能够被放置。这两个函数都会设置拖放区的dropEffect为copy,这意味着它会接受全部effectAllowed为copy的可拖动元素。(咱们的元素刚好就是这样的)。我如今说起这点是由于若是他们不匹配,drop event就不会触发,并且拖动也不会被取消。

收集drop数据

当咱们在右侧拖放时,咱们使用getData方法来提取text/url-list和text/htm的数据配置。若是没有(在用IE时)咱们就只提取text数据。

根据拥有的数据不一样咱们也须要作一些差别化的设置。若是咱们使用dataHTML则代表咱们在使用彻底支持的浏览器,咱们可使用全部的可拖拽节点。若是这样,咱们就将全部元素添加到放置区域,这样就完成了放置。

加入没有使用彻底支持的浏览器,咱们须要克隆在dragStart evnet中的dragItem来获取节点。而后将添加到放置区已完成放置。

  1. // called when draggable is dropped on droppable
  2. function drop(event) {
  3.  
  4. drop = this;
  5. $(drop).removeClass('drop-active');
  6.  
  7. var dataList, dataHTML, dataText;
  8.  
  9. // collect our data (based on what browser support we have)
  10. try {
  11. dataList = event.dataTransfer.getData('text/uri-list');
  12. dataHTML = event.dataTransfer.getData('text/html');
  13. } catch (e) {
  14. dataText = event.dataTransfer.getData('text');
  15. }
  16.  
  17. // we have access to the HTML
  18. if (dataHTML) {
  19. $(drop).empty();
  20. $(drop).prepend(dataHTML);
  21.  
  22. // check if this element is in the right spot
  23. checkCorrectDrop(drop, dragItem);
  24.  
  25. // see if the final image is complete
  26. checkCorrectFinalImage();
  27. }
  28.  
  29. // only have access to text (old browsers + IE)
  30.  
  31. else {
  32. $(drop).empty();
  33. $(drop).prepend($(dragItem).clone());
  34.  
  35. // check if this element is in the right spot
  36. checkCorrectDrop(drop, dragItem);
  37.  
  38. // see if the final image is complete
  39. checkCorrectFinalImage();
  40. }
  41.  
  42. event.preventDefault();
  43. event.stopPropagation();
  44. }
 

 完成这个游戏

两个drop evnet都调用了checkCorrectDrop(drop,dragItem)和checkCorrectFinalImage()函数。他们被用来为咱们的游戏服务。

checkCorrectDrop()函数用来检查自定义属性data-value是否和drag元素以及drop区域一致。若是一致,那么说明这块碎片是放在这里的,那么将会用绿色border高亮。

checkCorrectImage()函数用来检查全部的拼图碎片是否都在正确的位置上。若是咱们的正确元素和可拖动元素同样多,那么说明这个拼图游戏完成了。

从其它标签页和本机移动数据

经过native API,你能够定义接收拖动元素的拖放区域。看起来一些jQuery UI也能实现,不过jQuery U没法实现从外部标签页,窗口或浏览器直接拖动元素到咱们的放置区域。

你或许已经在不少低昂看过这个功能了。有很多网站容许你从一个标签页拖动一张图片到放置区,同时接收网站会考虑其中相关的交互动做。

为了使用dragging和dropping,须要对拖放区进行配置好让其明白如何承载将接受的数据。(基本上任何可拖动元素,好比图片,文本,连接和内容都会被拖动到拖放区。)

从本地拖动内容到一个网页并自动上传是其中一个革命性的功能,没了这个功能你都不知道该怎么办了。

大部分CMS(好比WordPress)支持经过drag-and-drop交互界面来上传内容。其它一些如Gmail之类的App也提供了此功能,让你可以直接将内容拖到一个区域而后自动粘贴或保存以备用。

例:从外部源拖动图片

下面的例子会承载来自其它标签页/窗口的拖放,让放置区采集图片用以展现。

若是你想知道它是怎么工做的,看看下面的demo.

点我看demo.

这个例子重点在于处理被拖动元素。不像别的例子,咱们必须对咱们作出的拖动配置数据,这里咱们只须要采集数据同时决定如何处理它就够了。

在咱们的drop函数中,咱们从使用getData(format)方法采集dataTransfer对象的数据开始。

  1. // get the URL of elements being dragged here
  2. try {
  3. dataValue = event.dataTransfer.getData('text/uri-list');
  4. dataType = 'text/uri-list';
  5. } catch (e) {
  6. dataValue = event.dataTransfer.getData('URL');
  7. dataType = 'URL';
  8. }
 

 咱们将这段放在异常处理中,主要是由于IE浏览器不能理解getData(),若是咱们试图使用的话,浏览器就会报错并结束运行。

若是咱们能够得到text/uri-list形式的数据,咱们就采集该数据;若是不能够,咱们就返回采用URL属性。

大部分被拖动元素,好比图片,连接或数据会有多种数据类型。由于咱们只关系这些元素的URL,因此能够正常工做。

若是咱们拥有了dataValue,这意味着用户在放置区放了一些东西。咱们如今须要弄清楚究竟是什么东西。咱们只想接收图片,可是API没法识别图片连接和普通连接,因此咱们须要作一些检查确保放进来的是图片。

  1. // determine if our URL is an image
  2. imageDropped = false;
  3. var imageExtensions = ['.jpg','.jpeg','.png','.bmp','.gif'];
  4. for (i = 0; i< imageExtensions.length; i++) {
  5. if (dataValue.indexOf(imageExtensions[i]) !== -1) {
  6. // create our image to add
  7. var image = '<img src="' + dataValue + '">';
  8. drop.append(image);
  9. imageDropped = true;
  10. break;
  11. }
 

 咱们建立了一个图片文件扩展名列表,好比.jpg和.png,而后检查连接中是否有出现这些扩展名。若是有,咱们能够确信这是一张图片,咱们就使用采集的值做为源绘制一张新的图片。

处理本地拖放元素

本地拖放元素有一点不同,咱们不使用getData(format)方法,咱们使用files()方法。这个方法会提供一个被放置的元素列表,这样咱们能够遍历这些元素。

  1. var dataFiles = event.dataTransfer.files;
  2. var dataOutput = [];
  3. if (dataFiles) {
  4. for (i =0; i < dataFiles.length; i++) {
  5. // do processing here
  6. }
  7. }
 

 好比咱们想遍历全部被放置元素以检查其中是否有图片。当咱们遍历每个文件的时候,咱们会处理一堆属性,其中就有type属性,该属性指出了这些元素扩展类型。

  1. // check if this is an image
  2. if (dataType.match('image.*')) {
  3. // it's an image, process further
  4. }
 

 若是匹配到了图片,咱们就建立一个新的fileReader对象用来将文件读入内存。而后使用readAsDataURL(item)方法写入咱们的文件,当完成后会出发onload event。咱们会在后面用到。

  1. // read into memory
  2. var reader = new FileReader();
  3.  
  4. // load element
  5. reader.readAsDataURL(dataItem);
 

 如今咱们要作的就是采集file reader的结果并将其添加进DOM。咱们就成功的将一个图片从本机拖动到网页中了。

  1. // when our image is loaded
  2. reader.onload = (function(theFile) {
  3. return function(e) {
  4. var url = e.target.result;
  5.  
  6. drop.append('<img src="' + url + '" title="' + dataName + '"/>');
  7. messageContainer.append('
  8. <p><strong>Successfully dropped an image from your desktop</strong></p>
  9. ');
  10. };
  11. })(dataItem);
 

 纵览浏览器支持状况

正如名字所传达的信息,这些API给开发者提供了一些event和method用以提供页面交互而不须要借助第三方的JavaScript库。

总的来讲桌面浏览器都支持可是移动端基本不支持,所以这些API在现代桌面浏览器上均可以正常运行。不过IE是个奇葩。

Chrome,Firefox,Safari和Opera的桌面端都有着使人惊叹的全面支持。不过IE的支持状况就取决于其版本了。好比:

  • IE7,IE8,IE9不支持dataTransfer.files或者.type对象。这意味着直到IE9都没法让用户从桌面拖动文件到网页。
  • 有限制的支持dataTransfer.setData/getData。在使用中,当咱们拖动元素时,咱们须要在drag中保存数据以供drop使用。在别的浏览器中能够保存为多种数据类型,包括自定义数据类型。可是IE只支持Text或者Url类型,这意味着限制了你处理数据的方式。
  • 任何版本的IE或Edge都不支持dataTransfer.setDragImage()方法。基本上没有办法设置一个自定义可拖动图片或元素。你只能使用浏览器默认的(大部分是一个半透明的图片副本)。

至于移动端,目前尚未对这些API有用的支持(2015.10)。这或许是由于移动端浏览器承载交互动做的方式。IE11是惟一会支持的移动端浏览器(我擦!)

附加内容

还有不少关于Drag和Drop API详细信息的资源。其中一些讨论了可用的方法和event在特定浏览器中的矛盾或者有一些不错的例子。

从这些连接开始吧,在我初探native drag和drop时经过他们收益良多。

总结

目前为止,你应该对native Drag 和 Drop API有一个大体的了解同时清楚如何经过它们来提供一个强交互界面。你或许应该多作练习来真正理解我在这里讨论的这些东西。

即便缺乏移动端的支持,桌面端浏览器仍是很强大,所以这依旧是一个在新项目中使用native API的好理由。

原文连接:http://www.gbtags.com/gb/share/9631.htm

相关文章
相关标签/搜索