[/code][b][/b]dojo支持拖放和移动操作,在dojo中采用了如下机制
[b]拖放[/b]
在 dojo的应用中,拖放是一个比较常见的操作。拖放操作最早的时候比较多的出现在桌面应用中,在传统的 Web 应用中比较少见。随着 Ajax 应用的流行,拖放操作越来越多的出现,可以提供与桌面应用类似的用户体验。在 Ajax 应用中实现拖放操作并不是一件容易的事情,需要考虑很多浏览器兼容性的问题。Dojo 核心库的 dojo.dnd模块提供了对拖放操作的良好支持。
dojo.dnd模块中包含了对两类拖放相关的操作的支持:一类是一般意义上的拖放,即拖拽页面上的一个元素并把它放到其它位置;另外一类是在页面上自由的移动某个元素。两类操作在实现上有所不同,下面会分别介绍。
[b]拖放操作[/b]
从拖放操作本身来说,需要一个源和一个目标。源和目标分别是通过 dojo.dnd.Source和 dojo.dnd.Target类来表示的。从实现上来说,dojo.dnd.Target继承自 dojo.dnd.Source。如果同时是源和目标的话,应该使用 dojo.dnd.Source来表示。在拖放的源中可以包含多个能够被拖动的条目,需要对这些条目进行管理。dojo.dnd.Container表示的是包含能被拖动的条目的容器,用来实现对条目的管理。对于 dojo.dnd.Container中包含的条目,需要有一种方式允许用户进行选择,比如一次选中一个或多个条目。dojo.dnd.Selector继承自 dojo.dnd.Container,并添加了与选择条目相关的功能。dojo.dnd.Source类则是继承自 dojo.dnd.Selector的。当用户拖拽一个包含在 dojo.dnd.Source中的条目在页面上移动的时候,需要以直观的方式告知用户条目的当前位置。dojo.dnd.Avatar用来实现这样的功能。除了上面提到的四个类之外,dojo.dnd.Manager用来管理整个拖放的过程。
在 Ajax 应用中使用拖放操作的时候,首先需要根据应用场景定义清楚拖放的源和目标。这些操作要符合用户的使用习惯,以免影响用户体验。比如用拖放操作来改变一个列表中元素的顺序,或是拖放所选择的物品到购物车中,这些都是比较合理的拖放操作的场景。完成这一步之后,就可以从表示拖放源的 DOM 节点中创建 dojo.dnd.Source对象了。dojo.dnd.Source的构造方法有两个参数:第一个参数 node表示的是拖放源的 DOM 节点,第二个参数 params是一个包含配置项的 JavaScript 对象。对于拖放源中所包含的条目,有抽象和具体两种表示方式。抽象的表示方式是一个 JavaScript 对象,其中包含的内容由应用自己来定义。具体的表示方式则是 DOM 节点。创建 dojo.dnd.Source对象的时候,需要传入一个 JavaScript 方法用来从抽象的 JavaScript 对象中创建出具体的 DOM 节点。该方法由 params对象的 creator属性来指定。该方法有两个参数,第一个 item表示的是代表拖放条目的 JavaScript 对象,第二个 hint用来说明创建出的具体 DOM 节点的用途,目前只支持一个值 avatar,表示创建的 DOM 节点是给 dojo.dnd.Avatar使用的。该方法需要返回一个包含属性 node、data和 type属性的 JavaScript 对象。其中属性 node表示的是创建出来的 DOM 节点,data表示的是抽象的 JavaScript 对象,type表示的是拖放条目的类型,用来判断是否可以被拖放到某个目标上。一般来说,拖放条目的 DOM 节点是作为拖放源节点的直接子节点的。如果想使用其它节点的话,可以通过 params对象的 dropParent属性来指定。
接下来就是在拖放源中添加拖放条目。这些条目有可能是固定的,也可能是动态变化的。对于条目固定的情况,可以直接以声明式的方式来增加条目。只需要在拖放源的 DOM 节点下定义包含 CSS 类 dojoDndItem的节点即可。dojo.dnd提供了默认的 creator方法来完成转换。每个节点本身、其属性 dndData和 dndType分别作为 creator方法返回值中的 node、data和 type属性的值。对于动态的条目,则需要使用 dojo.dnd.Source提供的 insertNodes(addSelected, data, before, anchor)方法。该方法的参数 addSelected表示添加的条目是否为被选中的状态;data表示的是包含条目抽象内容 JavaScript 对象的数组,会交给 creator方法来创建具体的 DOM 节点;before和 anchor用来表示条目节点的插入位置,anchor表示的是参考节点,而 before表示是插入的位置在参考节点之前还是之后。
创建了拖放源和其中的条目之后,下一步就是创建拖放目标。创建 dojo.dnd.Target的实例就可以表示一个拖放目标。创建目标的时候,通过第二个参数 params对象的属性 accept可以设置该目标接受的条目类型。只有当 accept 属性的值与创建条目时 creator方法返回值中 type属性的值相匹配的时候,拖放才能完成。这样就可以进行基本的拖放操作。代码清单 4 中给出了使用 dojo.dnd实现的简单拖放操作。
[code="html"]
<div id="source">
<div class="dojoDndItem" dndType="myItem"> 测试条目 1</div>
<div class="dojoDndItem" dndType="myItem"> 测试条目 2</div>
<div class="dojoDndItem" dndType="myItem"> 测试条目 3</div>
</div>
<div id="target">
</div>
var source = new dojo.dnd.Source("source");
var target = new dojo.dnd.Target("target", {
accept : ["myItem"]
});
代码清单 4 中给出了采用声明式的方式实现的简单拖放操作。默认情况下,被拖拽的条目会从拖放源中删除,添加到拖放目标中。如果希望只是从拖放源复制到目标的话,在创建 dojo.dnd.Source的时候,可以指定 params的属性 copyOnly的值为 true。
如果希望定制拖放条目被拖动时的显示方式,需要利用 creator方法创建出所需的节点。代码清单 5 中给出了动态添加拖放条目和定制条目拖动时显示方式的一个示例。
var source = new dojo.dnd.Source("source", {
creator : function(item, hint) {
var n;
if (hint == "avatar") {
n = dojo.create("div", {
innerHTML : dojo.replace("<span>{title}</span>", item)
});
}
else {
n = dojo.create("div", {
innerHTML : dojo.replace("<h4>{title}</h4><div>{description}</div>", item)
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
});
}
return {
node : n, data : item, type : ["book"]
};
},
copyOnly : true
});
var target = new dojo.dnd.Target("target", {
creator : function(item, hint) {
return {
node : dojo.create("span", {
innerHTML : item.title
}),
data : item,
type : ["book"]
};
},
accept : ["book"]
});
source.insertNodes(false, [
{title : "图书 1", description : "一本不错的书。"},
{title : "图书 2", description : "这也是一本好书。"}
]);
如 代码清单 5 所示,注意其中 insertNodes()方法和 creator方法的参数 hint的使用。当条目被拖动到目标上方并放下的时候,表示目标的 dojo.dnd.Target对象会首先通过 checkAcceptance(source, nodes)方法来检查是否允许拖动的条目放下。如果允许的话,目标上的 onDrop(source, nodes, copy)方法会被调用,其三个参数 source、nodes和 copy分别表示拖放源、被拖动的节点以及是否需要进行复制。默认提供的 onDrop()方法的实现会根据拖放源和目标是否相同来调用不同的方法:当源和目标不相同的时候,调用的是 onDropExternal(source, nodes, copy);相同的时候调用的则是 onDropInternal(nodes, copy)。拖动的节点在目标上被放下之后的默认行为取决于在创建目标 dojo.dnd.Target对象的时候,是否定义了 creator方法。如果定义了 creator方法,就通过此方法在拖放目标中创建出新的 DOM 节点;如果没有的话,就直接进行 DOM 节点的复制或移动。从 代码清单 5 中可以看到,拖放目标 target定义了 creator方法,因此当条目从 source拖放到其上之后,条目的显示方式发生了变化。
在有些拖放操作中,当条目在目标上放下的时候,并不需要进行 DOM 节点的复制或移动。拖放行为本身只是作为一种触发的动作。这个时候就需要自定义 onDrop()或是 onDropExternal()和 onDropInternal()方法的实现。代码清单 6 中给出了一个自定义拖动条目被放下时行为的示例。该示例的场景是用户拖动想要购买的图书到购物车中,购物车会自动更新图书的总价。
var sum = 0;
var target = new dojo.dnd.Target("target", {
accept : ["book"],
onDropExternal : function(source, nodes, copy) {
for (var i = 0, n = nodes.length; i < n; i++) {
var node = nodes[i];
var item = source.getItem(node.id);
sum += item.data.price;
}
dojo.byId("target").innerHTML = "总价合计为:" + sum + " 元";
}
});
如 代码清单 6 中所示,在 onDropExternal方法中,通过 source.getItem()方法来获取被放下的条目的抽象表示,即 creator方法返回的结果。通过得到的条目的抽象表示,就可以获取到所需的数据。有了所需的数据,就可以更新图书的总价。
最后要介绍的是 dojo.dnd.Manager的使用。在每个页面上只存在一个 dojo.dnd.Manager类的实例。通过 dojo.dnd.manager()可以获取该实例。该实例负责协调拖放操作,其中维护了一些与当前正在进行的拖放相关的状态,包括 source、target、avatar、nodes和 copy等。
当与拖放相关的操作发生时,dojo.dnd会发布一些主题,应用可以监听这些主题来执行额外的逻辑。这些主题包括:
/dnd/start:当开始拖动的时候,发布此主题。
/dnd/drop:当拖动的条目被放下的时候,发布此主题。
/dnd/cancel:当拖放操作被取消的时候,发布此主题。
/dnd/source/over:当正在拖动的条目移动到某个拖放目标的上方时,发布此主题。
/dnd/drop/before:在拖放的条目被放下之前,发布此主题。
[b]移动操作[/b]
在 Ajax 应用中,允许用户自由的移动页面上的元素也是一个常见的操作。比如在画图的应用中允许用户自由的摆放绘制元素。针对这样的需求,dojo.dnd模块中提供了 dojo.dnd.Moveable和 dojo.dnd.Mover,其中 dojo.dnd.Moveable表示的是可以被移动的元素,dojo.dnd.Mover是移动操作的具体执行者。
确定好需要进行移动的 DOM 节点之后,就可以创建出一个 dojo.dnd.Moveable对象来表示它。dojo.dnd.Moveable的构造方法有两个参数:node和 params,其中 node表示的是 DOM 节点,params表示的是与配置相关的 JavaScript 对象。比较重要的配置项有 handle,用来表示拖动时的手柄,默认是当前节点。当用户在手柄的区域内点击并拖动的时候,就可以移动此节点。在移动的过程中,dojo.dnd.Moveable的一些方法会被调用。应用可以通过覆写这些方法来添加额外的处理。这些方法包括:
onFirstMove(mover):第一次被移动的时候调用。参数 mover表示用来移动节点的 dojo.dnd.Mover对象。
onMoveStart(mover):当开始移动的时候调用。
onMoveStop(mover):当停止移动的时候调用。
onMove(mover, leftTop):当每次移动的时候调用。参数 leftTop表示将要移动到的位置的坐标。
onMoving(mover, leftTop):由 onMove()方法在完成移动之前调用。
onMoved(mover, leftTop):由 onMove()方法在完成移动之后调用。
dojo.dnd.Mover的实例只有在移动的时候才会存在,一般由 dojo.dnd.Moveable来自动创建。如果希望提供自己的 dojo.dnd.Mover的实现,可以通过 dojo.dnd.Moveable构造方法的 params参数的 mover属性来指定。代码清单 7 中给出了自由移动节点的一个示例。
var position = dojo.cookie("position");
if (position) {
var p = dojo.fromJson(position);
dojo.style("box", {
left : p.l + "px",
top : p.t + "px"
});
}
var moveable = new dojo.dnd.move.parentConstrainedMoveable ("box", {
area : "content",
within : true
});
dojo.connect(moveable, "onMove", null, function(mover, leftTop) {
dojo.cookie("position", dojo.toJson(leftTop));
});
在 代码清单 7 中使用了 dojo.dnd.move.parentConstrainedMoveable,这是 dojo.dnd提供了几个辅助类之一。它用来限制节点移动时不能超出其父节点的范围。与之类似的还有:dojo.dnd.move.boxConstrainedMoveable,用来限制移动范围在某个矩形区域内;dojo.dnd.TimedMoveable用来限制两次移动之间的时间间隔。
关于 dojo.dnd模块的更多介绍,见 参考资料。在介绍完与拖放操作相关的内容之后,下面介绍与处理常用数据类型相关的内容。
摘选自[url]http://www.360doc.com/content/10/1012/14/3103730_60358757.shtml[/url]