使用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

相關文章
相關標籤/搜索