dojo實現的兩種拖拽方式(拖動、拖放)打賞

Web2.0的時代,網頁中常常會用到拖拽的效果,這里了解下使用dojo來實現拖拽。在開始嘗試了解dojo拖拽效果以前,必須明確拖拽具有兩種截然不同的表現效果。

第一種表現效果是圖標被拖拽到哪里,其就會被直接放到哪里,這個拖拽效果是圖標完全緊跟拖拽的動作,與每一個拖拽動作的運動軌跡完全契合,這種效果被稱為“ 拖動”。第二種表現效果是當圖標被拖拽到一個地方,松開鼠標的時候,圖標會以當前位置為基礎而以其它圖標為參照系進行位置的自動調整。這種效果被稱為“拖放”。

dojo的拖動

“拖動”與“拖放”相比較,原理更加容易理解,使用更加簡單。而且更加貼近于人們直觀印象中的“拖拽”效果。

最簡單的拖動實例

要在 dojo 的支持下,實現拖動的效果所需要的只是使用 dojo 所提供的 dojo 標簽屬性標注出希望實現拖動效果的實體。簡單的說,就是如果希望一個實體可以拖動,則只需要在這個實體的標簽里面加上 dojoType=“dojo.dnd.Moveable”這個屬性。例如要實現一個表格的拖動,則只需要在這個表格的聲明標簽“<table>”里加上 dojoType=“dojo.dnd.Moveable”這個屬性。甚至就是在“<tr>”或“<td>”標簽中加上 dojoType=“dojo.dnd.Moveable”,也可以實現對應實體的拖動效果。

清單 1

<script type="text/javascript">
dojo.require("dojo.dnd.move"); //引入dojo的拖動功能模塊
dojo.require("dojo.parser"); //引入解析dojo標記的功能模塊
</script>

<table dojoType="dojo.dnd.Moveable">
<tbody>
    <tr>
        <td>Haha, I am a good guy.</td>
    </tr>
</tbody>
</table>
<!--引入dojoType="dojo.dnd.Moveable",讓上面的表格可以拖動-->

需要注意的是靜態創建可拖動實體需要引入 dojo.require("dojo.parser") 。

動態實現可拖動實體

在清單 1 中,通過在一些實體的標簽里面加上相應的 dojo 標簽屬性來實現可拖動實體的創建。這種靜態實現可拖動實體的方法簡單明了。但是在更多的情況下,往往需要根據一些實際情況運行得到的數據來動態的創建可拖動實體。在這種情況下,靜態實現可拖動實體的方法就不能滿足當下的需求。值得慶幸的是 dojo 對于所有靜態實現的方法都基本對應有一套相應的動態實現方法。

清單 2

<script type="text/javascript" src="dojo_path/dojo/dnd/move.js"></script>
<script type="text/javascript">
dojo.require("dojo.dnd.move");
var m1;
var init = function(){
    m1 = new dojo.dnd.Moveable("bad",{});//申明 id 為 "bad" 的實體為可拖動實體
};
dojo.addOnLoad(init);// 表示在頁面加載完成以后,執行 init 函數
</script>
<div id="bad">
You can cop me "Haha, I am a bad guy."
</div>

需要注意的是 dojo.dnd.Moveable("bad",{}) 中的大括號用來設置可拖動實體“bad”的一些與拖動相關的屬性,目前可以暫時設為空,則不設置任何與拖動相關的屬性。在后面的講述中,一些相關的重要屬性將被逐步介紹。

拖動柄
如果運行清單 1 和清單 2 中的代碼,然后嘗試在其頁面中使用鼠標去選擇可拖動實體中的內容。就會發現,無論使用何種方法都無法選擇可拖動實體中的內容,當然就更談不上復制可拖動實體中的內容了。

仔細分析無法選擇可拖動實體中內容的原因,就會發現如果要選擇頁面中的某一部分內容,其動作步驟為,按住鼠標左鍵不放,然后拖動鼠標選擇一塊區域作為確定選擇的內容;而如果要拖動一個可拖動實體,其動作步驟也為,按住鼠標左鍵,然后拖動鼠標引起可拖動實體的移動。

因此如果讓某個實體具有了可拖動的功能,則當對這個實體點下鼠標左鍵,并拖動鼠標時,就瀏覽器看來,其將不能理解這個動作的目的是要拖動該實體還是選擇該實體里面的內容。因為這兩個具有不同含義的動作就其動作本身來說是一模一樣的,瀏覽器沒有辦法對這兩個動作進行區分。

但現實的情況往往需要一個實體既可以被拖動,又可以被選擇其內部所包含的內容。 dojo 通過給可拖動實體增加一個拖動柄,實現了選擇內容動作和拖動實體動作的區分。

聲明拖動柄的方法為在聲明可拖動實體的時候,在可拖動實體的標簽中再加上一個除 dojoType 之外的另外一個 dojo 標簽屬性 handle= “”,handle 后面的雙引號中需要填入的是作為拖動柄部分的那個實體的 id 值。

清單 3

<div id="bad" dojoType="dojo.dnd.Moveable" handle="dragme">
<!--- 聲明 id 為 "dragme" 的實體為拖動柄 ->
<div id="dragme">You can drag it by me</div>
< ! —上面是拖動柄的實體 -->
You can cop me "Haha, I am a bad guy."
</div>

如果要動態聲明上面的“拖動柄”,則需要在頁面加載完成以后通過執行 dojo.dnd.Moveable("bad", {handle: "dragme"}) 來實現。handle 后面所跟的值為將成為可拖動實體“bad”拖動柄的實體 id 。

在清單 2 中,沒有設置可拖動實體的任何屬性。在清單 3 中設置了與拖動相關的其中一個屬性——拖動柄。

限制可拖動實體拖動的范圍

dojo 限制用戶可拖動實體活動范圍的方法有兩種。第一種方法為在生成可拖動實體的時候,給其設置一個邏輯上的活動范圍空間;第二種方法是依據頁面的一個實體,建立可拖動實體的活動范圍空間。需要注意的是,在第一種方法中的活動范圍空間是一個不存在相應頁面實體的定義,即這個所謂的活動范圍空間在頁面是實際不存在的。首先要討論的是第一種方法的使用。

清單 4

<script type="text/javascript">
dojo.require("dojo.dnd.move");
dojo.require("dojo.parser");
var init = function(){
    new dojo.dnd.move.boxConstrainedMoveable("aa", {box: {l: 100, t: 100, w: 500,
    h: 500}});// 定義可拖動實體 "aa" 和拖動的范圍 box
    new dojo.dnd.move.boxConstrainedMoveable("bb", {box: {l: 100, t: 100, w: 500,
    h: 500}, within: true});
};
dojo.addOnLoad(init);
</script>
<div id="aa">aaa</div>
<div id="bb">bbb</div>
<div id="cc" dojoType="dojo.dnd.move.boxConstrainedMoveable" box="{l: 100, t: 100, w: 500,h: 500}">ccc</div>
<div id="dd" dojoType="dojo.dnd.move.boxConstrainedMoveable" box="{l: 100, t: 100, w: 500, h: 500}" within="true">ddd</div>

在清單 4 中,將動態定義和靜態定義的方法都對比著寫了出來,前兩個可拖動實體是使用的動態定義的方法,后兩個實體所實現的效果和前面一樣但使用的是靜態定義的方法。

實現可拖動實體活動范圍的第一種方法是通過定義一個實際不存在的矩形框來作為可拖動實體的“監獄”。例如在清單 4 可拖動實體“aaa”中,{box: {l: 100, t: 100, w: 500, h: 500}} 是指在“aaa”外建立一個盒子區域,該盒子區域為“aaa”的活動范圍。“l: 100, t: 100”表示該盒子區域距頁面左邊界和上邊界各為 100px,“w: 500”表示盒子區域的寬度為 500px,“h: 500”表示盒子區域的高度為 500px 。需要注意的是盒子區域的 l 和 t 始終是以頁面邊界為標準。

圖 1
dojodnd1
圖 1 顯示出了清單 4 中可拖動實體“aaa”活動范圍的“盒子區域”。

可拖動實體“within”的屬性可設為“true”或者“false”,如果設為“true”的話表示“可拖動實體”的任何一個部分都不能超出其盒子區域的范圍,如果設為“false”表示只要拖動實體的左上角不超出盒子區域的范圍便為合法操作。
圖 2
dojodnd2
圖 2 中可拖動實體的“within”屬性為“false”,因此當前的位置是合法的。但是可能在某些時候,需要使用到更細致的活動范圍控制,或者需要根據可拖動實體的 DOM 父節點確定其活動范圍,而不是距頁面的邊距。對于這些情況,可以采用 dojo 提供的第二種限制用戶可拖動實體活動范圍的方法。

dojo 限制用戶可拖動實體活動范圍的第二種方法是在所有可拖動實體的外面創建一個 DOM 父節點,然后利用該父節點的“Layout”屬性將可拖動實體的運動范圍限制在父節點內。以此為基礎,依據父節點四種不同的“Layout”屬性分為 “margin”類型的限制,“border”類型的限制,“padding”類型的限制,和“content”類型的限制。 ( “margin”,“border”,“padding”和“content”是 Web 頁面中一個實體的 CSS 屬性 ) 。

清單 5

<style type="text/css">
.parent{
    background: #BFECFF;
    border: 10px solid lightblue;
    width: 500px;
    height: 500px;
    padding: 10px;
    margin: 10px;
}
</style>
<script type="text/javascript">
dojo.require("dojo.dnd.move");
dojo.require("dojo.parser");
function init(){
    new dojo.dnd.move.parentConstrainedMoveable("bad", {area: "padding", within: true});
    <!-- 該可拖動實體的范圍不能超過父節點的 padding 區域邊界 -->
    new dojo.dnd.move.parentConstrainedMoveable("good", {area: "content", within: true});
    <!-- 該可拖動實體的范圍不能超過父節點的 content 區域邊界 -->
};
dojo.addOnLoad(init);
</script>
<div class="parent">
<div style="background:red" dojoType="dojo.dnd.move.parentConstrainedMoveable" area="margin" within="true">I am restricted within my parent's margins.</div>
<!-- 該可拖動實體的范圍不能超過父節點的 margin 區域邊界 -->
<div style="background:green" dojoType="dojo.dnd.move.parentConstrainedMoveable"
area="border" within="true">I am restricted within my parent's border.</div>
<!-- 該可拖動實體的范圍不能超過父節點的 border 區域邊界 -->
<div style="background:blue" id="bad">I am restricted within my parent's paddings.</div>
<div style="background:pink" id="good">I am restricted within my parent's content.</div>
</div>

在清單 5 中聲明了四個可拖動實體,其中兩個是以靜態 dojo 標簽屬性的方式聲明,另外兩個是以動態的方式聲明。

捕獲拖動中的事件

dojo 通過事件訂閱發布機制實現了方便易用的捕獲拖動事件 API 。 dojo 在拖動開始、結束和過程中會發出一些消息,如果希望監測到頁面內可拖動實體的拖動開始事件和拖動結束事件可以通過訂閱“/dnd/move/start” 和“/dnd/move/stop”來實現

清單 6

<script type="text/javascript">
dojo.require("dojo.dnd.move");
dojo.require("dojo.parser");
dojo.addOnLoad(function(){
	dojo.subscribe("/dnd/move/start",function(node){// 訂閱開始拖動事件
	console.debug("Start move", node)
});
dojo.subscribe("/dnd/move/stop",function(node){// 訂閱拖動結束事件
console.debug("Stop move", node);});
});
</script>
<div dojoType="dojo.dnd.Moveable">Haha, I am a bad guy.</div><br /><br />
<p dojoType="dojo.dnd.Moveable">Haha, I am boy</p>

清單 6 的運行效果在 Firebug 下最易于觀察。在 Firebug 的 Console 窗口中,每一次拖動可拖動實體,就會在 Console 窗口中有相對應的輸出。

如果在某些時候,只希望監測某一個可拖動實體的事件,而不是所有可拖動實體的事件。則可以采用將可拖動實體的事件和一個函數連接起來以實現只監測某個特定可拖動實體的事件。

清單 7

<script type="text/javascript">
dojo.require("dojo.dnd.move");
dojo.require("dojo.parser");
dojo.addOnLoad(
function(){
	var boy = new dojo.dnd.Moveable("boy");
    dojo.connect(boy, "onMoveStart", function(mover){
		console.debug("Start moving boy", mover);
    });
    dojo.connect(boy, "onMoveStop", function(mover){
		console.debug("Stop moving boy", mover);
    });// 通過事件連接捕獲了可拖動實體的事件。 "mover" 是與可拖動實體相關的對象,里面包含了相關的屬性和方法
});
</script>
<body><p id="boy">Haha, I am boy</p></body>

在清單 7 中將可拖動實體“boy”和一個特定的輸出函數連接了起來。這樣在捕獲可拖動實體“boy”的時候,不會“驚動”其它可拖放實體。

dojo的拖放

拖放是一個復雜而又充滿魅力的功能。 dojo 支持拖放功能的原因,就是因為 dojo 的使用者在實際項目中開發高級拖拽操作功能的時候提出了這樣的需求。

最簡單的拖放實例

請讀者將下面的代碼在自己的機器上運行,并嘗試感受拖放的實際效果。

清單 8

<html>
<head>
<title>test</title>
<script type="text/javascript" src="dojo_path/dojo/dojo.js" djConfig="isDebug:true, parseOnLoad: true"></script>
<style type="text/css">
@import "dojo_path/dojo/resources/dnd.css";
</style>
<script type="text/javascript">
dojo.require("dojo.dnd.Source");
dojo.require("dojo.parser");
</script>
</head>
<body>
<table><tbody><tr>
<td>
<div dojoType="dojo.dnd.Source" style="border: 1px solid black; height: 200px width: 300px;">SOURCE</div>
<div class="dojoDndItem" style="height:50px;width:100%;background-color:blue">
<div class="bluesquare">BLUE</div>
</div><!-- 定義一個顏色為藍色的矩形可拖放實體 -->
<div class="dojoDndItem" style="height:50px;width:100%;background-color:red">
<div class="redsquare">RED</div>
</div><!-- 定義一個顏色為紅色的矩形可拖放實體 -->

<!-- 定義了一個容器,該容器為可拖放實體在拖放開始前,可拖放實體存放的地方。將其名定義為源容器 -->
</td>
<td>
<div dojoType="dojo.dnd.Target" style="border: 1px solid black; height: 200px; width: 300px; ">TARGET</div>
<!-- 定義了一個容器,該容器為可拖放實體拖放后,可拖放實體存放的地方。將其名定義為目標容器 -->
</td>
</tr></tbody></table>
</body>
</html>

由清單 8 可以得知,對于拖放來說,可拖放實體必須是從一個容器中拖放到另外一個容器中。可拖放實體是不能存在于容器之外的任何地方。如果可拖放實體從“容器 A”出來,放入“容器 B”中,則一般習慣上稱 A 為源容器,B 為目標容器。

被拖放的實體稱之為“可拖放實體”。要實現可拖放實體的拖放,頁面中必須要有“拖放源容器”和“拖放目標容器”。拖放源容器為起初可拖放實體存放的地方,而拖放目標容器為可拖放實體拖起后可以放的地方。

如果運行清單 8 中的代碼,就會發現在拖起可拖放實體的時候,有一個與“原可拖放實體”很相似的小圖標在隨著鼠標移動,其被稱為可拖放實體的“替身”。“替身”的主要作用有兩個,第一是指明目前操作的實體是哪一個或是有哪幾個實體被操作,第二是作為原實體的替身,幫助用戶判斷目前選中可拖放實體的鼠標所處的位置為哪里,判斷該區域是否為合法的拖放區域。

替身是由“原可拖放實體”轉換而來,包含了“原可拖放實體”的主要外貌特征,同時替身的細微化,大大減小了如果采用“原拖動實體”作為標識而帶來的系統負擔和提高了標識的精確度。

圖 3
dojodnd3
拖動的本質是可拖動實體象素位置的變化,而拖放的本質是一個頁面 DOM 結構的變化。實體象素位置的變化,可以只通過修改這個實體的屬性來實現,不用和頁面的任何其它部分打交道。但是對于拖放,當將一個可拖放實體從源容器拖放到目標容器時,就是將該可拖放實體先從源容器的 DOM 節點上刪除,再在目標容器的 DOM 節點上加上可拖放實體。

因此可拖放實體的存在,在拖放動作前必須依賴于一個父 DOM 節點,這就是源容器,拖放動作后也必須依賴于一個新的父 DOM 節點,這就是目標容器。

在某些情況下,可能需要的是能在幾個容器之間將可拖放實體不斷的拖來拖去。那么所做的修改是只需要將源容器和目標容器都用 dojoType= “dojo.dnd.Source”來進行聲明就能實現可拖放實體從目標容器拖回源容器的操作。

動態生成源容器、目標容器和可拖放實體

動態聲明源容器和目標容器的方法比較簡單,動態創建源容器的方法為 var foo=new dojo.dnd.Source(Node, Params),而動態創建目標容器的方法為 var foo=new dojo.dnd.Target(Node, Params) 。在聲明源容器的方法中,Node 為要聲明為源容器的 DOM 節點 id,Params 所代表的是一些用來確定容器相關屬性的參數。

清單 9

function init(){
    mysource = new dojo.dnd.Source("mysource",{});// 存放可拖放實體的 " 源容器 "
    mytarget = new dojo.dnd.Target("mytarget",{});// 存放可拖放實體的 " 目標容器 "
}
<table><tbody><tr>
<td>
<div id="mysource" style="border: 1px solid black; height: 200px; width: 300px;">SOURCE
<div class="dojoDndItem" style="height:50px;width:100%;background-color:blue">
<div class="bluesquare">BLUE</div>
</div><!-- 定義一個顏色為藍色的矩形可拖放實體 -->
<div class="dojoDndItem" style="height:50px;width:100%;background-color:red">
<div class="redsquare">RED</div>
</div><!-- 定義一個顏色為紅色的矩形可拖放實體 -->
</div><!-- 定義了一個容器,該容器為可拖放實體在拖放開始前,可拖放實體存放的地方。
將其名定義為源容器 -->
</td>
<td>
<div id="mytarget" style="border: 1px solid black; height: 200px; width: 300px;">TARGET</div>
</td>
</tr></tbody></table>

在清單 9 中動態聲明了源容器和目標容器。由清單 9 中觀察可知,容器的聲明是建立在已經存在的頁面實體基礎之上的。

清單 10

function init(){
    mysource = new dojo.dnd.Source("mysource");// 存放可拖放實體的 " 源容器 "
    mysource.insertNodes(false,["Do you love me?","Good good study","<div style='height:50px;width:100%;background-color:blue'></div>"]);
    mytarget = new dojo.dnd.Target("mytarget");// 存放可拖放實體的 " 目標容器 "
}
<table><tbody><tr>
<td>
<div id="mysource" style="border: 1px solid black; height: 200px; width:300px;">SOURCE</div>
<!-- 定義了一個容器,該容器為可拖放實體在拖放開始前,可拖放實體存放的地方。將其名定義為源容器 -->
</td>
<td>
<div id="mytarget" style="border: 1px solid black; height: 200px; width: 300px; ">TARGET</div>
</td>
</tr></tbody></table>

在清單 10 中通過 dojo.dnd.Source.insertNodes 來將動態聲明的字符串化的實體插入到“源容器”中。如果回顧一下前面關于拖放基本原理的內容,就可以知道拖放的過程就是刪除一個 DOM 節點和重新創建一個 DOM 節點的過程。刪除一個 DOM 節點對于不同的應用沒有太大的差異性,但是創建一個 DOM 節點對于不同的應用情況可能就需要不同的創建方式。

在大多數情況下,拖放后在目標容器里重建一個可拖放實體的 DOM 節點是調用 dojo 的一個默認構造函數。這個構造函數能夠在目標容器內完全重建一個與源容器一模一樣的可拖放實體。

但如果希望可拖放實體在拖放入目標容器以后發生變化,與在源容器中的可拖放實體不一樣,則可以通過創建自己的構造函數來實現。

如果要創建自己的構造函數,首先要在構建源容器和目標容器的時候,指明自己要創建的構造函數的函數名。

dojo.dnd.Source("mysource",{creator: sourcenodecreater, copyOnly: false}) 表示創建存放可拖放實體的“源容器”。 dojo.dnd.Target("mytarget",{creator: targetnodecreater, copyOnly: false}) 表示創建存放可拖放實體的“目標容器”。而“creator: sourcenodecreater” 表示源容器中構建可拖放實體的函數名為“sourcenodecreater”,“creator: targetnodecreater”表示目標容器中構建可拖放實體的函數名為“targetnodecreater”。

首先來創建一個最簡單的名為“sourcenodecreater”的構造函數。

清單 11

function sourcenodecreater(data, hint){
    var myitem = dojo.doc.createElement_x("div");
    myitem.id = dojo.dnd.getUniqueId();
    myitem.innerHTML = data;
    return {node: myitem, data: data};
}

注意清單 11 中源容器的構造函數“sourcenodecreater”只是一個例子,但其包含了一個構造函數最基本的要素。在這里只是為了闡述清楚最基本的原理。具體的功能和如何構造出所需要的可拖放實體,需要根據需求來進行分析設計。

對于“sourcenodecreater”函數所接受的兩個參數,“data”所代表的是接受到的關于可拖放實體的一些特征數據。因為可拖放實體是動態構造的,所以在很多情況下,可拖放實體要根據系統前面所傳來的數據來構造相應的可拖放實體,而“data”所包含的就是這些用來構造可拖放實體的數據。 “hint”是與替身有關的參數。
* var myitem = dojo.doc.createElement_x("div") 表示創建一個 div 節點。可拖放實體將在這個節點上創建。
* myitem.id = dojo.dnd.getUniqueId() 表示將會給可拖放實體一個唯一的 id 。
* dojo.dnd.getUniqueId() 可以在一次系統運行中每次產生一個絕對獨一無二的 id 。因為可拖放實體的拖放是 DOM 節點的銷毀和重建,因此,當一個可拖放實體的拖放完成以后,其 id 將會發生變化。
* myitem.innerHTML = data 是用來構建可拖放實體的實際效果,目前這里只是簡單的將數據作為字符串在可拖放實體中展現。
* return {node: myitem, data: data} 表示在上面的工作完成以后,將創建的節點和創建節點所使用的數據返回給 inserNode 函數,由其完成將該可拖放實體插入到 DOM 樹中的相應位置。

在實際的情況中,往往需要在一個可拖放實體拖入目標容器后發生變化。例如在線購買東西時,選中希望購買的東西,將其拖入購物車后,一般代表商品的圖片將會變小許多。那么就其前面所了解的,在可拖放實體進入目標容器的時候,將調用“targetnodecreater”函數來構造在目標容器中的實體。那么如果希望其發生變化的話,就必須得在“targetnodecreater”上做些文章。比如希望任何可拖放實體進入目標容器后都變為字符串“111”。

清單 12

function targetnodecreater(data, hint){
    var myitem = dojo.doc.createElement_x("div");
    myitem.id = dojo.dnd.getUniqueId();
    myitem.innerHTML = "111";
    return {node: myitem, data: data};
}

清單 12 的代碼中創建了一個任何可拖放實體進入目標容器后都將變為字符串“111”的構造函數。

拖放柄

同拖動操作一樣,拖放操作也有自己的拖放柄。其拖放柄存在的原因和意義,這里也不再詳細敘述。

要給一個可拖放實體增加一個拖放柄需要完成兩步操作。
* 第一步,是在可拖放實體中增加一個拖放柄實體并將其 Class 聲明為 class= “dojoDndHandle”。
* 第二步,是在容器中增加 withHandles= “true”這樣一個屬性。

在完成了上述兩步以后,可拖放實體的拖放操作只能通過抓住拖放柄來實現,對可拖放實體的其余部分則無法進行拖放操作。

清單 13

<div class="dojoDndItem" style="height:50px;width:100%;background-color:blue">
<span class="dojoDndHandle" style="background-color:yellow">Handle</span>
<!-- 藍色矩形的拖放柄 --><div class="bluesquare">BLUE</div>
</div><!-- 定義一個顏色為藍色的矩形可拖放實體 -->
清單 13 是通過靜態方法實現可拖放實體的拖放柄,緊接著介紹動態創建可拖放實體拖放柄的方法。

清單 14
function init(){
    mysource = new dojo.dnd.Source("mysource",{creator: sourcenodecreater, copyOnly: false,
    withHandles: true});// 存放可拖放實體的 " 源容器 ",其中聲明 withHandles 的值為“true”
    mysource.insertNodes(false,["Do you love me?",
    "<span class='dojoDndHandle' style='background-color:yellow'>Handle
    </span>Good good study","<span class='dojoDndHandle' style='background-color:yellow'>
    Handle</span><div style='height:50px;width:100%;background-color:blue'></div>"]);
    // 通過直接寫入靜態標簽屬性 class='dojoDndHandle' 構建“拖放柄”
    mytarget = new dojo.dnd.Target("mytarget",
        {creator: targetnodecreater, copyOnly: false}
    );
    // 存放可拖放實體的 " 目標容器 "
}

清單 14 是動態創建拖放柄的一個完整的實例。

可拖放實體的替身

可拖放實體的替身是一個體積細微,但又作用重大的部分。替身分為兩個部分,上面的一個部分是“頭”,下面的一個部分是“身體”。
圖 4
dojodnd4
圖 4 清楚的表明了替身的頭部分和身體部分,其中紅色框內的為“頭”部分,綠色框內的為“身體”部分。對于替身身體形式的定義,可以在可拖放實體的構造函數中完成。對于替身頭形式的定義,則需要通過 CSS 來完成。

如果要對替身的身體部分進行修改,則需要 hint 幫助 (hint 為拖放實體構造函數兩個參數中的一個 ) 。替身身體的本質也是調用可拖放實體的構造函數來構建的。因此如果需要構建特定的替身身體,就需要在可拖放實體的構造函數里面來做文章。

所有的可拖放實體包括替身的身體部分都是通過可拖放實體的構造函數來構建的。那么帶來的一個問題是構造函數如何判斷,某次調用可拖放實體的構造函數是構建可拖放實體還是替身呢?這時候 hint 可以被用來幫助進行區分。如果 hint 的值不等于“avatar”的話,則說明是構建可拖放實體,反之就是說明構建的是替身。

清單 15

if(hint!="avatar")
    myitem.innerHTML = data;
else
    myitem.innerHTML = "Haha,my avatar";

清單 15 表示當 hint 判斷不是“avatar”的時候,根據傳入的 data 的值構建可拖放實體,當判斷是“avatar”的時候,替身的身體部分就寫入“Haha,my avatar”。在能夠按照自己的意愿構造替身的身體部分后,下一步想到的是改變替身的頭部分。在 dojo 設計 dnd 替身的時候,其暴露給使用者的只是對身體的修改,但是在個別情況下,可能希望美化替身,例如想對替身的頭進行修改。這時候需要操作的是 CSS 。
在 dojo_path/dojo/resources 有一個 dnd.css 文件,在這個文件中,定義了 dnd 操作的一些頁面效果。如果期望對替身的頭進行修改的話,就必須在當前頁面中重新定義與其相關的 CSS 定義。例如如果想將替身頭部分的背景顏色重新定義為藍色。則可以在當前頁面的 head 部份寫入 “.dojoDndAvatarHeader {background: blue;}”。

清單 16

<style type="text/css">
@import "../../resources/dnd.css";
dojoDndAvatarHeader{background: blue;}
</style>

清單 16 是將替身頭部分修改為藍色的實例。要實現修改后效果,需將其放入當前頁面的 head 部分內。

捕獲拖放中的事件

dojo 將其認為可能常用的一些事件進行了注冊,并將這些注冊的事件以相對應的名稱發布出來。
* /dnd/start:當拖放開始的時候監測到該事件,能獲取的相關值包括當前源容器、拖放的節點和判斷這次操作是否是復制操作的布爾值。
* /dnd/source/over:當鼠標滑入或滑出一個容器的時候,監測到該事件。需要注意的是當滑入或滑出一個容器的時候都會獲得容器。但如果從一個容器滑入到容器外,得到的第二個容器值為空。如果從一個容器直接滑入到另外一個容器,得到的第二個容器值不為空。
* /dnd/drop/before:該方法只被 dojo1.1.0 或更高的版本所支持。在 drop 發生之前監測到該事件。換句話,可以在該方法中定義一些功能,這些功能將在 drop 發生之前的一剎那發生。
* /dnd/drop:當放下可拖放實體的時候,可監測到該事件。
* /dnd/cancel:當拖放動作取消,或者可拖放實體被拖放到一個無效區域時,可監測到該事件。

清單 17

function init(){
	mysource = new dojo.dnd.Source("mysource",{creator: sourcenodecreater, copyOnly:false});// 存放可拖放實體的 " 源容器 "
	mysource.insertNodes(false,["Do you love me?","Good good study","<div style='height:50px;width:100%;background-color:blue'></div>"]);
	mytarget = new dojo.dnd.Source("mytarget",{creator: targetnodecreater, copyOnly:false});// 存放可拖放實體的 " 目標容器 "
}
function sourcenodecreater(data, hint){
	var myitem = dojo.doc.createElement_x("div");
	myitem.id = dojo.dnd.getUniqueId();
	if(hint!="avatar")
		myitem.innerHTML = data;
	else
		myitem.innerHTML = "Haha,my avatar";
	return {node: myitem, data: data};
}
function targetnodecreater(data, hint){
	var myitem = dojo.doc.createElement_x("div");
	myitem.id = dojo.dnd.getUniqueId();
	myitem.innerHTML = "111";
	return {node: myitem, data: data};
}
dojo.subscribe("/dnd/start",
function(source,nodes,iscopy){
	console.debug(source);console.debug(nodes);console.debug(iscopy);
});// 注冊開始事件,當拖放動作開始時,便會有輸出
dojo.subscribe("/dnd/source/over",
function(source){
	console.debug(source);
});// 注冊鼠標滑過容器事件,當鼠標滑過容器的時候,便會有輸出
dojo.subscribe("/dnd/drop/before",
function(source,nodes,iscopy){
	console.debug(source);console.debug(nodes);console.debug(iscopy);
});// 注冊結束前事件,當拖放動作接受前時,便會有輸出
dojo.subscribe("/dnd/drop",
function(source,nodes,iscopy){
	console.debug(source);
	console.debug(nodes);
	console.debug(iscopy);
	console.debug("bad");    
});// 注冊結束事件,當拖放動作結束時,便會有輸出
dojo.subscribe("/dnd/cancel", function(){
	console.debug("cancel");
});// 注冊取消事件,當拖放動作取消時,便會有輸出
dojo.addOnLoad(init);

在清單 17 中,監測了拖放操作中的開始、結束、取消、結束前和鼠標滑過容器五個動作,并將這些動作函數接受到的值進行了輸出。在實際項目中對拖放事件的操作就是建立在監測這些事件和這些事件輸出值的基礎之上的。所不同的只是不同的處理方法和不同的處理順序。

對于監聽事件函數的輸出值,“source”表示源容器;“nodes”表示進行拖放操作的“可拖放實體們”(“nodes”是一個數組);“iscopy”為“true”或“false”,表示這次操作是否是復制操作。

dojo實現的兩種拖拽方式(拖動、拖放)
文章《dojo實現的兩種拖拽方式(拖動、拖放)》二維碼
  • 微信打賞
  • 支付寶打賞

已有9條評論

  1. 分切機刀片

    我覺得要在 dojo 的支持下,實現拖動的效果所需要的只是使用 dojo 所提供的 dojo 標簽屬性標注出希望實現拖動效果的實體。

    2012-12-15 16:59 回復
  2. 大學驛站

    做了幾年的博客,一直不敢走寫程序的路

    2012-12-14 23:08 回復
  3. 潤初顏

    代碼最暈了!

    2012-12-13 09:56 回復
  4. 虛擬機

    看不明白,自身問題

    2012-12-10 15:08 回復
  5. 籃球比分

    好長的代碼啊

    2012-12-10 13:03 回復

(必填)

(必填)

(可選)

黑龙江22选5开奖