拖拽:从Dojo到HTML5
Dojo 及 HTML 5 简介
Dojo 是目前最流行的开源 JavaScript 工具库之一,很多开发者以及企业用户都把Dojo 作为首选的 JavaScript 工具。Dojo 为 Web 应用的开发提供了大量的客户端组件,能够让你可以方便的进行 HTML DOM 操作、拖拽、AJAX 调用、定制可视化控件等来使得你的 Web 应用变成富网络应用 (RIA)。而且 Dojo 在性能、可访问性、多语言支持以及文档方面都做的非常出色,这也是企业选择 Dojo 的原因之一。
HTML 5 是最新一代的 HTML,它将成为 HTML、XHTML 以及 HTML DOM 的新标准, HTML 5 是 W3C 与 WHATWG 合作的结果,目前仍外于开发中 ; 自从上一代 HTML4,Web 世界已经发生了巨大的变化,HTML 5 的到来将更大的促进 Web 的发展,HTML 5 提供了很多新的功能,主要有:
◆新的 HTML 元素,例如 section, nav, header, footer, article 等
◆用于绘画的 Canvas 元素
◆用于多媒体播放的 video 和 audio 元素
◆用于定位的 Geolocation API
◆本地存储以及离线应用
◆Web Workers
◆拖拽 API
◆文件 API
我们主要对 HTML 5 的拖拽功能进行讲解,并结合文件 API 与桌面进行交互。来与 Dojo 的 dnd 拖拽组件进行比较。
使用 Dojo 创建及定制拖拽应用
类似 Dojo 其他组件,拖拽的实现有两种方式:声明式和编程式。在这里我们使用声明式的方式做简要的介绍。
在 Dojo 拖拽实现中,有两个重要的元素 dojo.dnd.Source 和 dojo.dnd.Target。这两个元素分别标示了拖拽中的源容器 Source 和目标容器 Target。值得注意的是源容器 Source 默认也是目标容器 Target,而不需要作目标容器 Target 的声明。我们在源容器 Source 中创建一些可以拖动的元素,要让这些元素可拖动,我们要为这些元素添加 class 属性值 DojoDndItem。下面的示例代码定义了一个源容器 Source 以及一些可以拖动的元素。在这些可以拖动的元素中我们可以定义它们的拖拽类型 dndType。dndType 的值开发者可以自己定义,而目标容器 Target 元素的 accept 属性定义了该目标容器接受的拖拽类型。
清单 1. 创建拖拽的源容器和可拖拽的元素
1. div id="source" style="height:400px;" dojoType="dojo.dnd.Source"
2. div id="item1" class="dojoDndItem item" dndType="divItem"item1/div
3. img src="w3c.jpg" class="dojoDndItem item" dndType="imageItem"/img
4. a href="http://www.w3.org/TR/html5/" class="dojoDndItem item" dndType="linkItem"
5. HTML5 specification/a
6. /div
对于 Target 我们可以创建一个 div,然后加上属性 dojoType=dojo.dnd.Target和属性 accept。不在 accept 中的类型的 dojoDndItem 元素将不被这个容器接受。例如清单 2 中的目标容器只接受 divItem 和 imageItem 这两种类型,那么清单 1 中的 linkItem 将不能被拖到这个目标容器中。
清单 2. 创建拖拽的目标容器和可接受的类型
1. div id="target" style="height:400px;" dojoType="dojo.dnd.Target"
2. accept="divItem, imageItem"
真正 Web 应用的拖拽没有这么简单,开发者往往需要在拖拽的过程中更多的介入。这时候可以通过对 Dojo 提供的 dojo.dnd.Source 和 dojo.dnd.Target 进行继承扩展,开发满足业务需要的功能和特性。这里将不赘述。
使用 HTML 5 创建拖拽应用
在这一章中,我们将要使用 HTML 5 创建一个简单的拖拽应用,如图 1 所示,用户可以把网页上内容从左边的区域拖放到右边的区域。这个应用程序的代码可到附件中可以下载。
图 1. HTML 5 拖拽应用效果图
创建可以拖动的节点
使用 HTML 5 创建拖拽只需要对可拖拽的节点进行声明给可以拖拽的节点添加 draggable 属性并设值为 true。如清单 3 中的 div 节点,通过添加 draggable 属性就可以拖拽了。在 HTML 5 中img和a 默认情况下是可以拖拽的,所以不需要设置 draggable 属性。
清单 3. 通过添加 draggable 属性来创建源容器中可以拖动的节点
1. div id="source" style="height:300px;"
2. div id="item1" class="item" draggable="true"item1/div
3. img id="item2" src="w3c.jpg" class="item"/img
4. a id="item3" href="http://www.w3.org/TR/html5/" class="item"HTML5 specification/a
5. /div
创建 HTML 5 拖拽的源容器和目标容器
在 HTML 5 中,我们需要给指定的节点来绑定一些事件来使之成为具有源容器或目标容器的功能。在 HTML 5 的拖拽过程中的事件有 7 个,分别是应用在目标容器或拖动节点上的 dragstart, drag, dragend 等 3 个事件,以及应用在目标容器节点上的dragenter, dragover, dragleave 和 drop 等 4 个事件。表 1 中对这些事件的触发机制和常见的操作进行了描述。
表 1. HTML 5 拖拽过程中可以绑定的事件
备注 : 在 Dojo 中所有 dnd 源容器或目标容器在拖拽开始时都会调用 onDndStart 事件方法,而在 HTML 5 中只有拖动的节点及源容器可以触发 dragstart 事件,其他容器包括目标容器在拖动开始时不会感知源容器及拖动节点的 dragstart 事件。
清单 4 展示给目标容器绑定 dragenter, dragover, dragleave, drop 事件的示例代码。在 dragenter 和 dragleave 事件中,我们对目标容器的背景样式进行修改使得用户感知目标容器的状态(如图 2 所示)。在 dragover 事件中我们对清单 3 中的链接元素(id 属性值为 item3)的节点进行了限制。drop 事件中我们要把拖动的节点插入到目标节点的 DOM 结构中。
清单 4. 创建目标容器的事件
1. var target = dojo.byId('target');
2. dojo.connect(target, 'dragover', function(e){
3. // doesn't allows link item (id = item3) to drop
4. if (e.dataTransfer.getData('id') != "item3"){
5. e.preventDefault();
6. }
7. });
8.
9. dojo.connect(target, 'dragenter', function(e){
10. //add style
11. dojo.addClass(target, "over");
12. });
13.
14. dojo.connect(target, 'dragleave', function(){
15. //remove style
16. dojo.removeClass(target, "over");
17. });
18.
19. dojo.connect(target, 'drop', function(e){
20. //remove style if drop is successful
21. dojo.removeClass(target, "over");
22.
23. // stops the browser from redirecting
24. if (e.stopPropagation) e.stopPropagation();
25.
26.
27. var itemId = e.dataTransfer.getData('id');
28. var dragItem = dojo.byId(itemId);
29. e.target.appendChild(dragItem);
30.
31. }
图 2. 当拖动节点到目标容器是时对目标容器进行高亮显示
从清单 4 中我们在目标容器上对事件对象的 dataTransfer 属性进行了 getData 操作取出了关键字 id 对应的数据。在 HTML 5 拖拽过程中,用户可以在表 1 定义的事件里通过 event.dataTransfer 得到 DataTransfer 对象 ( 详见 W3C 网站上的接口定义)并对其进行定制传输数据、定制拖拽影像等操作。例如我们可以在 dragstart 事件中通过 setData 方法初始化数据(代码详见附件)。表 2 中列出了这各数据对象的方法及常用的用途。
表 2. DataTransfer 的常用方法
与桌面进行交互
除了在网页中对一些页面上的元素进行拖拽以外,HTML 5 扩充的 API 还允许网页与文件系统进行交互,比如从文件系统拖一个或几个文件到网页中,或是从网页拖到文件系统中。以前者为例,当我们从桌面或其它文件夹拖动文件到网页上某个目标结点时,我们可以通过 DataTransfer 的 files 属性得到这些文件数量以及文件的属性及内容。DataTransfer.files.length 的大小即为拖动文件的数量,当没有拖动文件时,files.length 的大小即为 0,可用来判断是否有文件拖动。
清单 5. 通过 dataTransfer.files 拿到文件对象
1. var files = e.dataTransfer.files;
2. var msg = "";
3. for (var i = 0; i files.length; i++) {
4. console.log ("Name: " + files[i].name + ", fileSize: " + files[i].size);
5. var dataReader = new FileReader();
6. dataReader.onload = function(){
7. msg += ("content: " + dataReader.result);
8. }
9. dataReader.readAsText(files[i]);
10. }
从清单 5 中的代码中我们可以看到 files 中存储了若干 file 对象,通过这个对象可以获取文件名,文件大小等。然后我们可以通过 FileReader 获取文件的内容。获取内容的 FileReader 并不是 HTML 5 拖拽的功能,而是借助了 File API。它可以以文本,二进制,以及 dataURL 的形式读取,实现读取文件内容实现文件上传等,在我们的示例代码 HTML 5dndfile.html 中我们演示了通过 readAsText 方法读取文本文件和通过 readAsDataURL 方法读取图像文件的使用。
与桌时行交互时,我们只需要对将清单 5 中给出的代码稍加修改加到目标容器的 drop 事件中,其它事件不用修改。例如清单 6 所示。
清单 6. 在目标容器的 drop 事件读取文本文件的内容
1. dojo.connect(textdiv, 'drop', function(e){
2. if (e.stopPropagation){
3. e.stopPropagation(); // stops the browser from redirecting
4. }
5. var files = e.dataTransfer.files;
6. var msg = "";
7. for (var i = 0; i files.length; i++) {
8. msg += ("Name: " + files[i].fileName + ", fileSize: " + files[i].fileSize);
9. var dataReader = new FileReader();
10. dataReader.onload = function(){
11. msg += ("content: " + dataReader.result);
12. textdiv.textContent = msg;
13. }
14. dataReader.readAsText(files[i]);
15. }
16. });
这样当我们拖动一个文本文件到指定的目标区域时,我们就可以看到文件内容。
Dojo 和 HTML 5 拖拽功能的比较和选择
Dojo 实现了一套完整的拖拽框架和事件机制,并提供了默认的实现,用户可以通过声明的方式快速实现拖拽,而且还可以通过继承默认的 Source、Target 以及 Avatar 实现拖拽定制化。从使用经验上来看,Dojo 更倾向于完整的 DOM 节点操作,而数据的传输往往是通过绑在 DOM 节点上的属性实现的。
HTML 5 的拖拽现在还在规范的定制和完善中,各个主流浏览器对该规范的支持也是各有千秋,基本上还处于发展的阶段。本文中提供的示例仅在 Firefox 3.6 以上版本测试通过。HTML 5 作为新时代的 HTML 协议,拖拽事件中的 DataTransfer 接口体现了拖拽过程中以数据传输为中心的发展前景。与此同时,跟 File API 的结合可以使得 Web 应用的数据交互通过拖拽操作延伸到最终用户的桌面及文件系统上。另外 HTML 5 还可以实现在不同浏览器窗口之间的拖拽操作,也是拖拽过程传输数据的一种应用。
小结
对比 Dojo 和 HTML 5,我们不难发现在使用 Dojo 比 HTML 5 可以更容易地开发出体验效果非常好的拖拽应用;而 HTML 5 作为 HTML 的新规范,注重了拖拽过程中数据传输的重要性。
两者如果可以融合则可以互补其短。通过实验,笔者发现 Dojo dnd 与 HTML 5 拖拽暂时不能在同一结点上同时使用,由于 Dojo 是在 HTML4 规范基础上的工具包,它们之间可能因在事件上的冲突会导致 HTML 5 拖拽不可用。但是我们有理由相信,随着 HTML 5 规范的发展,将会有支持 HTML 5 拖拽的工具包出现,届时开发者可以更为便捷的开发出更为丰富的 Web 应用。