如何在ASP.NET网站中使用HTML 5拖放功能
拖放操作在桌面应用程序中司空见惯。现在的Web应用程序也试图利用拖放操作的简易性和强大功能,提供改善的用户体验。Web开发人员经常借助基于JavaScript的库或自定义方法,以便在自己的应用程序中能够实现拖放操作。幸运的是,HTML5本身内置了支持拖放的功能。你使用拖放功能,可以拖动某个HTML元素,将它拖放到另一个HTML元素中。在此过程中,还可以将数据从源元素传送到目标元素。如果把拖放操作与服务器端处理集成起来,你就可以提供丰富的用户体验。本文介绍了如何在ASP.NET网站中使用HTML5的拖放功能。
为HTML元素启用拖动功能
要使用HTML5的拖放功能,第一步是让一个或多个元素可以拖动。为此,你只需要将HTML元素的可拖动属性设成true。比如说,下面这行标记代码将<DIV>元素设成了可拖动元素:
<div class="myclass" draggable="true">Some content</div>
拖放事件
将一个或多个DOM元素标为可拖动元素只完成了一部分工作。想让你的拖放操作实际可以使用,而且对最终用户有视觉吸引力,就要处理某些事件。下面列出了这些事件:
事件 | 描述 |
dragstart | 拖动操作开始时,该事件被触发。 |
drag | 元素拖动时,该事件被触发。 |
dragenter | 可拖动元素被拖动,并输入有效的拖放目标后,该事件被触发。 |
dragleave | 被拖放到有效拖放目标的可拖动元素离开拖放目标后,该事件被触发。 |
dragover | 可拖动元素被拖放到有效拖放目标上方后,该事件被触发。 |
drop | 已拖动元素被拖放到有效拖放目标上面后,该事件被触发。 |
dragend | 拖动操作结束后,该事件被触发。 |
你可以两种方法将事件处理函数连接到这些事件,即在DOM元素标记中,使用onxxxx语法,或者使用JavaScript(或基于JavaScript的库,如jQuery)。下列标记和代码显示了这两种方法。
<div class="myclass" draggable="true" ondragstart="OnDragStart" ondrop="OnDrop"></div> $("div").each(function () { this.addEventListener('dragstart', OnDragStart, false); this.addEventListener('drop', OnDrop, false); });
要注意上述代码如何使用jQuery代码中的addEventListener()方法来连接事件处理函数。
拖操作与放操作之间传送数据
大多数时候,拖动某个元素,然后把它拖放到另外某个元素上也需要在源元素与目标元素之间传送一些数据。为了完成这项数据传送任务,HTML5提供了DataTransfer对象。下列表格列出了DataTransfer对象的一些重要属性和方法。
属性/方法 | 描述 |
effectAllowed | 表明所允许操作的类型。可能的值是:none、copy、copyLink、 copyMove、link、linkMove、move、all和uninitialized。 |
dropEffect | 表明目前选择的操作的类型。如果操作类型得不到effectAllowed 属性的支持,那么操作就失效。可能的值是:none、copy、link和move。 |
setDragImage () | 设置拖动操作期间显示的特定元素。 |
setData() | 设置所传送的特定数据。 |
getData() | 检索之前设置的数据,以便进一步处理。 |
clearData() | 清除之前存储的数据。 |
你通常会在dragstart和drop事件处理函数中使用dataTransfer对象的属性和方法。
执行拖放操作
现在不妨把你到目前为止获得的信息放入到一个简单而实用的应用程序中。先建立一个新的ASP.NET网站。你将创建一个酷似下图的简单的Web表单:
简单的Web表单
注意:该实例在最新版的Firefox上经过了测试,但是应该也可以在最新版的其他主要浏览器上运行。正如你所见,Web表单表示一辆简单的购物手推车。各产品由放在DataList控件里面的DIV元素来表示。这些产品可以拖放到购物袋上。一旦所有需要的产品添加完毕,你可以点击“Place Order”按钮,即可将产品数据发送到服务器、下订单。上面这个例子使用了如下所示的Entity Framework数据模型。你可以从本文所附的代码下载链接(http://developer.com/imagesvr_ce/6920/drag_drop_Code.zip)获得SQL Server Express数据库和数据模型。
该例子使用了Entity Framework数据模型。
上面所示的数据模型只包括基本的细节。在实际环境下的购物手推车系统中,你可以捕捉到多得多的细节。产品目录是一个DataList控件,它的ItemTemplate包括一个可拖动<DIV>元素。该DIV包装了某个产品的所有产品细节。
<asp:DataList ID="DataList1" runat="server" DataSourceID="EntityDataSource1" RepeatDirection="Horizontal"> <ItemTemplate> <div class="product" draggable="true"> <header><%# Eval("Name") %></header> <div><asp:Image runat="server" ID="img1" ImageUrl='<%# Eval("ImageUrl") %>' /></div> <div><%# Eval("Description") %></div> <br /> <div><%# Eval("Cost","Cost : ${0}") %></div> </div> </ItemTemplate> </asp:DataList> <div class="bag"> <asp:Image runat="server" ID="img1" ImageUrl="~/images/cart.jpg" /> <br /><br /> <input id="Button1" type="button" value="Place Order" /> <br /> <input id="Button2" type="button" value="Clear Cart" /> </div>
请注意表示产品的DIV元素如何被标以设成true的可拖动属性。负责处理产品外观和感觉的product CSS类如下所示:
.product { height: 300px; width: 150px; float: left; border: 2px solid #666666; background-color: white; padding:3px; margin:5px; text-align: center; cursor: move; }
购物手推车还是带CSS类bag的DIV元素。
.bag { padding:10px; text-align: center; cursor: move; }
下一步是将拖放事件处理函数连接到各个元素。将使用jQuery来完成这一步,所以确保在<head>部分中引用了jQuery库。
<script src="scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
连接各个事件处理函数的jQuery代码会在ready()事件处理函数中编写,如下所示:
$(document).ready(function () { $("div .product").each(function () { this.addEventListener('dragstart', OnDragStart, false); }); $("div .bag").each(function () { this.addEventListener('dragenter', OnDragEnter, false); this.addEventListener('dragleave', OnDragLeave, false); this.addEventListener('dragover', OnDragOver, false); this.addEventListener('drop', OnDrop, false); this.addEventListener('dragend', OnDragEnd, false); }); })
正如你所见,第一个each()调用为dragstart事件添加了事件侦听函数。所有的产品DIV元素应该会处理该事件,那样代码就会根据CSS类product来过滤元素。同样,购物手推车元素应该会处理其他事件,尤其是drop事件。
下列标记代码显示了完整的事件处理函数:OnDragStart、OnDragEnter、OnDragLeave、OnDragOver、OnDrop和OnDragEnd。
function OnDragStart(e) { this.style.opacity = '0.3'; srcElement = this; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', $(this).find("header")[0].innerHTML); } function OnDragOver(e) { if (e.preventDefault) { e.preventDefault(); } $(this).addClass('highlight'); e.dataTransfer.dropEffect = 'move'; return false; } function OnDragEnter(e) { $(this).addClass('highlight'); } function OnDragLeave(e) { $(this).removeClass('highlight'); } function OnDrop(e) { if (e.stopPropagation) { e.stopPropagation(); } srcElement.style.opacity = '1'; $(this).removeClass('highlight'); var count = $(this).find("div[data-product-name='" + e.dataTransfer.getData('text/html') + "']").length; if (count <= 0) { $(this).append("<div class='selectedproduct' data-product-name='" + e.dataTransfer.getData('text/html') + "'>" + e.dataTransfer.getData('text/html') + "</div>"); } else { alert("This product is already added to your cart!"); } return false; } function OnDragEnd(e) { $("div .bag").removeClass('highlight'); this.style.opacity = '1'; }
让我们逐个详细介绍上面显示的每个事件处理函数。
OnDragStart
function OnDragStart(e) { this.style.opacity = '0.3'; srcElement = this; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', $(this).find("header")[0].innerHTML); }
dragstart事件处理函数减少了被拖动元素的不透明度,那样最终用户就能获得关于拖动操作的视觉线索。拖动操作的来源存储在全局变量srcElement中,因为我们以后在drop事件处理函数中需要它。dataTransfer对象的effectAllowed属性被设成了move。此外,setData()方法将数据设成了传送到dataTransfer对象中header元素(即产品名称)的innerHTML。这样一来,drop事件处理函数就知道哪个产品添加到购物手推车中。setData()方法的第一个参数表明了所传送数据的类型(这里是“text/html”)。
OnDragOver
function OnDragOver(e) { ... $(this).addClass('highlight'); e.dataTransfer.dropEffect = 'move'; return false; }
dragover事件处理函数将CSS类添加到拖放目标,以便为最终用户提供关于这一操作的视觉线索。higlight CSS类如下所示:
.highlight { background-color:Yellow; }
OnDragOver函数也将dataTransfer对象的dropEffect设成move。
OnDragEnter和OnDragLeave
function OnDragEnter(e) { $(this).addClass('highlight'); } function OnDragLeave(e) { $(this).removeClass('highlight'); }
dragenter和dragleave事件处理函数很简单,只是将highlight CSS类添加到目标元素,或者从目标元素中清除这个类。
OnDrop
function OnDrop(e) { ... srcElement.style.opacity = '1'; $(this).removeClass('highlight'); var count = $(this).find("div[data-product-name='" + e.dataTransfer.getData('text/html') + "']").length; if (count <= 0) { $(this).append("<div class='selectedproduct' data-product-name='" + e.dataTransfer.getData('text/html') + "'>" + e.dataTransfer.getData('text/html') + "</div>"); } else { alert("This product is already added to your cart!"); } return false; }
drop事件处理函数将源元素的不透明度设回成了1,因为拖放操作已完成。它还清除了目标元素中的highlight CSS类。然后,它将被拖动的产品添加到目标元素后面。注意使用getData()方法,检索之前在dragstart事件处理函数中设置的数据。还要进行检查,确保同一产品不能添加多次。
OnDragEnd
function OnDragEnd(e) { $("div .bag").removeClass('highlight'); this.style.opacity = '1'; }
dragend事件处理函数只是清除拖放目标中的highlight CSS类。
将数据从客户端传送到服务器
要将购物手推车中的产品实际传送到服务器端代码,你就要使用jQuery的$.ajax()方法。“Place Order”的click事件处理函数拥有相关编码,如下所示:
$("#Button1").click(function () { var data = new Array(); $("div .bag div").each(function (index) { data[index] = "'" + this.innerHTML + "'"; }); $.ajax({ type: 'POST', url: 'shoppingcart.aspx/PlaceOrder', contentType: "application/json; charset=utf-8", data: '{ products:[' + data.join() + ']}', dataType: 'json', success: function (results) { alert(results.d); }, error: function () { alert('error'); } }); });
正如你所见,$.ajax()调用驻留在ShoppingCart.aspx Web表单里面的Web方法PlaceOrder。PlaceOrder Web方法如下所示。
[WebMethod]
public static string PlaceOrder(string[] products) { Guid orderId = Guid.NewGuid(); DatabaseEntities db = new DatabaseEntities(); foreach (string p in products) { Order order = new Order(); order.OrderId = orderId; order.ProductName = p; order.Qty = 1; db.Orders.AddObject(order); } db.SaveChanges(); return "Order with " + products.Length.ToString() + " products has been added!"; }
PlaceOrder Web方法只是把订单细节放入到Orders表格中。PlaceOrder() Web方法接受一组表示产品名称的字符串。要注意$.ajax()如何传送采用JSON格式的产品参数。Web方法一旦成功完成,success处理函数就会向最终用户显示提醒信息。现在运行Web表单,将一个或多个产品拖放到购物手推车上,然后点击“Place Order”按钮,就可以将产品名称从购物手推车传送到服务器。
结束语