Web開發中的Drag&Drop完全手冊

    這幾天做了一些drag&drop操作方面的工作,在這裏把一些注意事項記錄下來,算是給自己備個忘,也給需要做類似工作的人留個樹陰。這裏要討論的drag&drop是指使用IE提供的內置機制,而不是使用鼠標模擬的那種"假"drag&drop,比如移動一個div或span的效果那種。

    要實現和控制drag&drop操作,那麼首先第一點要弄清楚的是,到底哪些元素是可以在Web上被drag的?實際上IE默認給我們並讓我們drag的元素並不多,它們是:圖片選中的文字(包括頁面文字和文字控件(input, textarea)中的文字)和連接(普通連接和錨點)。除此之外,別的Web元素默認都不支持drag操作(在這些元素上面drag其實就是選擇操作了),所以要實現對元素默認的drag&drop控制,只能選這3類元素來操作。

    
在BizStruct同學的斧正中提到,其實幾乎所有的Html元素都是支持drag&drop的(見其回覆),在此表示由衷的感謝。

    接下來,那麼哪些元素又是可以接受drop操作呢?任何頁面上的可見元素都是可以接受drop操作的,而它們之間的不同只是在於默認的drop事件不一樣。比如,文字控件(input, textarea)的默認drop事件就是獲取drag操作傳過來的文字內容;iframe元素的默認drop操作是到航道drag操作傳過來的URL地址。當然絕大多數的Web元素的默認是操作是do nothing,什麼也不做。

    那麼當進行drag&drop操作時,有那些是可控制和定製的內容呢?這裏關於drag&drop提供了以下一些事件(我們把它們分爲作用於來源對象目的對象來分別討論),先討論主要作用於來源對象的事件:
   ·ondrag —— 在整個從drag動作開始,直道drop動作結束的過程中,都會觸發的一個事件。
   ·ondragstart —— 在drag動作開始時,在來源對象上觸發的一個事件。
   ·ondragend —— 在drop動作結束的時候,在來源對象上出發的一個事件。

   而主要做要在目的對象上的事件:
   ·ondragenter —— 在drag動作進入某一有效目的元素時,在該目的元素上觸發的一個事件。
   ·ondragover —— 在drag動作進入某一有效目的元素後,在該目的元素上觸發的一個事件。
   ·ondragleave —— 在drag動作離開某一有效目的元素時,在該目的元素上觸發的一個事件。
   ·ondrop —— 在任何有效目的元素上進行drop操作時,在該目的元素上觸發的一個事件。

    這裏的來源和目的的劃分不是絕對的,比如ondragover事件,在drag操作過程中,如果鼠標進入了來源對象中,同樣的也會觸發這個事件。這些事件觸發的順序是:來源對象 --> ondragstart --> ondrag --> ondragend;目的對象 --> ondragenter --> ondragover --> ( ondragleave | ondrap )。由於是分別在同一個對象上觸發的事件,所以這個順序很簡單。那麼對於一個完整的從來源對象到目的對象的drag&drop操作來說,事件的觸發序列又是怎樣呢?如果src表示來源對象,des表示目的對象,那麼事件觸發序列爲:

    src:ondragstart --> src:ondrag --> des:ondragenter --> des:ondragover --> ( des:ondragleave | des:ondrop ) --> src:ondragend.

    示例爲:
Drag Source Drop Destination
    // 如果alert窗口不響應鼠標點擊可以使用鍵盤的space鍵來確定窗口
<onmouseover="status=this.innerText; return true;" ondragend="alert('src:on'+event.type)" onclick="return false;">Drag Source</A>
<SPAN ondrop="alert('des:on'+event.type)" ondragleave="alert('des:on'+event.type)">Drop Destination</SPAN>

    瞭解了事件觸發順序後,定製drag&drop過程中鼠標的光標形狀也是非常重要的一個內容。因爲用戶的drag&drop的整個過程都需要靠鼠標光標的形狀指導其進行操作,如果不能實時的調整光標爲適合的型狀,drag&drop操作對用戶來說將無異於朦眼尋物。IE提供了用來控制的drag&drop過程中光標形狀的兩個屬性,它們是:effectAllowed和dropEffect。

    其屬性分別爲:
        ·effectAllowed: copy, link, move, copyLink, copyMove, all, none & uninitialized.
        ·dropEffect: copy, link, move, none.

    前者effectAllowed是用來控制允許drag&drop操作類型的,所以這裏的effect不是顯示的"效果",而是是否可以執行的"操作",並且該屬性只能在ondragstart事件中進行初始化,之後再對其賦值將無效。當然如果只使用effectAllowed屬性,就已經可以達到控制光標形狀的作用了。只是effectAllowed屬性在處理複合操作時,比如copyLink, copyMove和all,會默認顯示靠前那個操作類型的鼠標類型。也就是說如果effectAllowed是copyMove,那麼這是鼠標光標就是copy形狀。這下就知道爲什麼還要弄個dropEffect屬性了吧?不過這個dropEffect屬性中指定的effect,只能是之前effectAllowed允許的操作類型範圍中的值,否則沒有效果(顯示no-drop鼠標光標)。
    
    示例爲:
Drag Source Drop Destination
<ondragstart="event.dataTransfer.effectAllowed='copyMove'; event.dataTransfer.dropEffect='move'" onmouseover="status=this.innerText; return true;" ondragover="event.dataTransfer.dropEffect='move'; event.returnValue=false; event.cancelBubble=true;" onclick="return false;">Drag Source</A>
<SPAN ondragover="event.dataTransfer.dropEffect='copy'; event.returnValue=false; event.cancelBubble=true;">Drop Destination</SPAN>

    查看代碼時注意,你會發現在src和des的對象元素中,在ondragover事件裏除了對dropEffect賦以適當的值外,還有兩句話:
   event.returnValue=false;
   event.cancelBubble
=true;

    只是由於頁面元素在接受dragover的時候,本身都有其默認的鼠標光標顯示型狀,所以爲了讓用戶自定義的鼠標光標生效,就需要使事件event的returenValue爲false值並停止當事件的冒泡(event.cancelBubble=true)。

    到目前爲止,一個完整的drag&drop過程就差數據傳遞了,其實忙活了半天,這纔是藏在所有交互操作和顯示效果下面最重要的步驟。這個過程需要藉助於IE提供的DHTML Data Transfer對象來完成,在window對象的屬性event對象中,分別有兩個Data Transfer對象各自的一個實例:一個叫dataTransfer,另一個叫clipboardData。這兩個對象實例的行爲非常相似,但又有一些區別,clipboardData顧名思義,它使用操作系統的剪貼板來存取數據,並有3個方法;而dataTransfer通過操作一個自己的內部剪貼板來存取數據(每次ondragend事件觸發後就自動清空了),除了有和clipboardData相同的3個方法外,還有兩個屬性(就是前面介紹的那兩個effectAllowed和dropEffect)。

    我們這裏不對clipboardData作更多的討論,繼續來看dataTransfer對象。它包含3個方法,它們是:setData(sDataFormat, sText), getData(sDataFormat)和clearData([sDataFormat])。它們更詳細的使用和參數請參閱MSDN,這裏我只用它們來實現drag&drop的數據傳遞。

    示例爲:
Drag Source Drop Destination
<ondragstart="event.dataTransfer.effectAllowed='copyMove'; event.dataTransfer.dropEffect='move'; event.dataTransfer.setData('TEXT', this.innerText);" onmouseover="status=this.innerText; return true;" ondragover="event.dataTransfer.dropEffect='move'; event.returnValue=false; event.cancelBubble=true;" onclick="return false;">Drag Source</A>
<SPAN ondrop="this.innerText = event.dataTransfer.getData('TEXT'); this.style.color = 'red';" ondragover="event.dataTransfer.dropEffect='copy'; event.returnValue=false; event.cancelBubble=true;">Drop Destination</SPAN>

    其實很簡單,就是在src的ondragstart中,調用event.dataTransfer.setData('TEXT', this.innerText),然後再des的ondrop事件中,調用this.innerText = event.dataTransfer.getData('TEXT')就行了。

 

發佈了147 篇原創文章 · 獲贊 0 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章