HTML5 添加了对拖放(drag and drop)的支持。我们之前只能依靠jQuery 这样的JavaScript库才能处理这种操作。把拖放内置到浏览器的好处是它可以正确的集成到操作系统中,而且正如将要看到的,它能跨浏览器工作。

1. 创建来源项目

我们通过 draggable属性告诉浏览器文档里的哪些元素可以被拖动。这个值有三个允许的值:

它的默认值是auto,即把决定权交给浏览器,通常来说这就意味着所有元素默认都是可拖动的,我们必须显示设置draggable 属性为false 来禁止拖动。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>定义可拖放项目</title>
<style>
#src > * {float: left;}
#target,#src > img {border: thin solid black;padding: 2px;margin: 4px;}
#target {height: 81px;width: 81px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
#target > img {margin: 1px;}
</style>
</head>
<body>
<div id="src">
<img draggable="true" id="apple" src="../imgs/apple.png" alt="apple" />
<img draggable="true" id="banana" src="../imgs/banana-small.png" alt="banana" />
<img draggable="true" id="lemon" src="../imgs/lemon100.png" alt="lemon" />
<div id="target">
<p>Drop Here</p>
</div>
</div>
<script>
var src = document.getElementById("src");
var target = document.getElementById("target");
</script>
</body>
</html>

此例里有三个img元素,每一个的draggable 的属性都被设为true。这里还创建了一个id为target的div元素,稍后将设置它用来接收我们拖动的img元素。从下图可以看到这个文档再来浏览器里的样子。

我们不需要再做任何设置就能拖动水果图像,但浏览器会提示我们不能把它们释放到任何地方。通常的做法是展示一个禁止进入的标准作为光标,如下图所示:

处理拖动事件

我们通过一系列事件来利用拖放功能

我们可以用这些事件在视觉上强调拖动操作,如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用针对被拖动元素的事件</title>
<style>
#src > * {float: left;}
#target,#src > img {border: thin solid black;padding: 2px;margin: 4px;}
#target {height: 81px;width: 81px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
#target > img {margin: 1px;}
img.dragged {background-color: lightgrey;}
</style>
</head>
<body>
<div id="src">
<img draggable="true" id="apple" src="../imgs/apple.png" alt="apple" />
<img draggable="true" id="banana" src="../imgs/banana-small.png" alt="banana" />
<img draggable="true" id="lemon" src="../imgs/lemon100.png" alt="lemon" />
<div id="target">
<p id="msg">Drop Here</p>
</div>
</div>
<script>
var src = document.getElementById("src");
var target = document.getElementById("target");
var msg = document.getElementById("msg");
src.ondragstart = function(e){
e.target.classList.add("dragged");
}
src.ondragend = function(e){
e.target.classList.remove("dragged");
msg.innerHTML = "Drop Here";
}
src.ondrag = function(e){
msg.innerHTML = e.target.id;
}
</script>
</body>
</html>

此例定义了一个新的CSS样式,它会被应用到属于dragged类的元素上。在dragstart事件触发时把拖动的元素添加到这个类中,在dragend事件触发时把它从类中移除。作为对drag事件的响应,这里把释放区里显示的文本设为被拖动元素的id值。在拖动操作过程中,drag事件每隔几毫秒就会触发以此,所以这不是最有效率的技巧,但它确实能演示这个事件。此例的显示效果如下:

2. 创建释放区

要让某个元素成为释放区,我们需要处理 dragenter和 dragover事件。它们是针对释放区的其中两个事件。

dragenter和 dragover 事件的默认行为是拒绝接受任何被拖放的项目,因此我们必须要做的最重要的事就是防止这种默认行为被执行。

PS:拖放功能的规范告诉我们还必须想要称为释放区的元素应用dropzone属性,而且此属性的值应当包含我们愿意接受的操作与数据类型细节。浏览器实际上不是这么实现拖放功能的。

修改前面例子的JavaScript代码如下:

<script>
var src = document.getElementById("src");
var target = document.getElementById("target");
var msg = document.getElementById("msg"); target.ondragenter = handleDrag;
target.ondragover = handleDrag; function handleDrag(e){
e.preventDefault();
} src.ondragstart = function(e){
e.target.classList.add("dragged");
}
src.ondragend = function(e){
e.target.classList.remove("dragged");
msg.innerHTML = "Drop Here";
}
src.ondrag = function(e){
msg.innerHTML = e.target.id;
}
</script>

添加这些代码后,我们就有了一个活动的释放区。当我们拖动一个项目到释放区元素上时,浏览器会提示如果我们放下时它就会被接受,如下图所示:

接受释放

我们通过处理drop事件来接收释放的元素,它会在某个项目被放到释放区元素上时触发。下面的例子展示了如何处理响应drop事件,具体的做法是使用一个全局变量作为被拖动元素和释放区之间的桥梁。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>处理drop事件</title>
<style>
#src > * {float: left;}
#target,#src > img {border: thin solid black;padding: 2px;margin: 4px;}
#target {height: 81px;width: 81px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
#target > img {margin: 1px;}
img.dragged {background-color: lightgrey;}
</style>
</head>
<body>
<div id="src">
<img draggable="true" id="apple" src="../imgs/apple.png" alt="apple" />
<img draggable="true" id="banana" src="../imgs/banana-small.png" alt="banana" />
<img draggable="true" id="lemon" src="../imgs/lemon100.png" alt="lemon" />
<div id="target">
<p id="msg">Drop Here</p>
</div>
</div>
<script>
var src = document.getElementById("src");
var target = document.getElementById("target");
var msg = document.getElementById("msg"); var draggedID; target.ondragenter = handleDrag;
target.ondragover = handleDrag; function handleDrag(e){
e.preventDefault();
} target.ondrop = function(e){
var newElem = document.getElementById(draggedID).cloneNode(false);
target.innerHTML = "";
target.appendChild(newElem);
e.preventDefault();
} src.ondragstart = function(e){
draggedID = e.target.id;
e.target.classList.add("dragged");
}
src.ondragend = function(e){
var elems = document.querySelectorAll(".dragged");
for (var i=0;i<elems.length;i++){
elems[i].classList.remove("dragged");
}
}
</script>
</body>
</html>

此例在dragstart事件触发时设置了变量draggedID 的值。这能够记录被拖动元素的id属性值。当drop事件触发时,用这个值克隆了被拖动的img元素,把它添加为释放区元素的一个子元素。其显示效果如下:

3. 使用DataTransfer对象

与拖放操作所触发的事件同时派发的对象是DragEvent,它派生于MouseEvent。DragEvent对象定义了Event与MouseEvent对象的所有功能,并额外增加了 dataTransfer 属性,用来返回用于传输数据到释放区的DataTransfer对象。

我们可以用DataTransfer对象从被拖动元素传输任意数据到释放区元素上。DataTransfer对象定义的属性和方法如下表所示:

在上个例子中,克隆了元素本身。但DataTransfer对象允许我们使用一种更为复杂的方式。我们能做的第一件事是用DataTransfer对象从被拖动元素传输数据到释放区,如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用DataTransfer对象传输数据</title>
<style>
#src > * {float: left;}
#target,#src > img {border: thin solid black;padding: 2px;margin: 4px;}
#target {height: 81px;width: 81px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
#target > img {margin: 1px;}
img.dragged {background-color: lightgrey;}
</style>
</head>
<body>
<div id="src">
<img draggable="true" id="apple" src="../imgs/apple.png" alt="apple" />
<img draggable="true" id="banana" src="../imgs/banana-small.png" alt="banana" />
<img draggable="true" id="lemon" src="../imgs/lemon100.png" alt="lemon" />
<div id="target">
<p id="msg">Drop Here</p>
</div>
</div>
<script>
var src = document.getElementById("src");
var target = document.getElementById("target"); target.ondragenter = handleDrag;
target.ondragover = handleDrag; function handleDrag(e){
e.preventDefault();
} target.ondrop = function(e){
var droppedID = e.dataTransfer.getData("Text");
var newElem = document.getElementById(droppedID).cloneNode(false);
target.innerHTML = "";
target.appendChild(newElem);
e.preventDefault();
} src.ondragstart = function(e){
e.dataTransfer.setData("Text", e.target.id);
e.target.classList.add("dragged");
}
src.ondragend = function(e){
var elems = document.querySelectorAll(".dragged");
for (var i=0;i<elems.length;i++){
elems[i].classList.remove("dragged");
}
}
</script>
</body>
</html>

此例在响应dragstart事件时用setData方法设置了想要传输的数据。第一个蚕食指定了数据的类型,它只支持两个值: Text和 Url。第二个参数是我们想要传输的数据(此例中是被拖动元素的id属性)。为了获取它的值,使用了getData方法,并把数据类型作为参数。

你可能会觉得奇怪:为什么这种方式比使用全局变量更好?答案是它能跨浏览器工作。这么说的意思不是指跨同一个浏览器了IDE窗口或标签页,而是横跨不同类型的浏览器。这意味着我们可以从 Chrome浏览器的文档拖动一个元素,然后再Firefox浏览器的文档里释放它,因为拖放功能的支持是集成在操作系统里的,有着相同的特性。如果你打开一个文本编辑器,输入单词 apple,选中它然后拖动到浏览器的释放区,你就会看到苹果的图像被显示出来,效果和我们拖动同一个文档里的某个img元素一样。效果如下:

3.1 根据数据过滤被拖动的项目

可以用DataTransfer对象里存放的数据来选择我们愿意在释放区接受哪些种类的元素。修改上一个示例的JavaScript代码如下:

<script>
var src = document.getElementById("src");
var target = document.getElementById("target"); target.ondragenter = handleDrag;
target.ondragover = handleDrag; function handleDrag(e){
if(e.dataTransfer.getData("Text") == "banana"){
e.preventDefault();
}
} target.ondrop = function(e){
var droppedID = e.dataTransfer.getData("Text");
var newElem = document.getElementById(droppedID).cloneNode(false);
target.innerHTML = "";
target.appendChild(newElem);
e.preventDefault();
} src.ondragstart = function(e){
e.dataTransfer.setData("Text", e.target.id);
e.target.classList.add("dragged");
}
src.ondragend = function(e){
var elems = document.querySelectorAll(".dragged");
for (var i=0;i<elems.length;i++){
elems[i].classList.remove("dragged");
}
}
</script>

此例从DataTransfer对象获得数据值,然后检查它是什么。此例表明只有当数据值是banana时才愿意接受被拖动的元素。这么做的效果是过滤掉了苹果和柠檬的图像。当用户拖动这些图像当释放区时,浏览器会提示它们不能被释放。

PS:这种过滤方式测试暂时不可用,估计和浏览器的支持度有关。

3.2 拖放文件

另一种隐藏在浏览器里的HTML5新功能被称为文件API,它允许我们使用本机文件,不过是以严格受控的方式。部分控制来自于我们通常不直接与文件API进行交互,而是使它通过其他功能显露出来,包括拖放功能。下面的示例展示了如何使用文件API,在用户从操作系统里拖动文件并放入释放区时做出响应。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>处理文件</title>
<style>
body > * {float: left;}
#target {border:medium double black;margin: 4px;height: 75px;width: 200px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
table {margin: 4px;border-collapse: collapse;}
th,td {padding: 4px;}
</style>
</head>
<body>
<div id="target">
<p id="msg">Drop Files Here</p>
</div>
<table id="data" border="1"></table>
<script>
var target = document.getElementById("target");
target.ondragenter = handleDrag;
target.ondragover = handleDrag;
function handleDrag(e){
e.preventDefault();
}
target.ondrop = function(e){
var files = e.dataTransfer.files;
var tableElem = document.getElementById("data");
tableElem.innerHTML = "<tr><th>Name</th><th>Type</th><th>Size</th></tr>";
for(var i=;i<files.length;i++){
var row = "<tr><td>"
+ files[i].name+"</td><td>"
+ files[i].type+"</td><td>"
+ files[i].size+"</td></tr>";
tableElem.innerHTML += row;
}
e.preventDefault();
}
</script>
</body>
</html>

当用户把文件放入释放区时,DataTransfer对象的文件属性会返回一个FileList对象。我们可以将它视为一个由File对象构成的数组,每个对象都代表用户释放的一个文件(用户可以选择多个文件然后一次性释放它们)。下表展示了File对象的属性:

此例里,script 枚举了放入释放区的文件,并在表格里显示了File属性的值。此例的运行效果如下:

在表单里上传被释放文件

我们可以结合拖放功能、文件API和用Ajax请求上传数据,让用户能从操作系统拖动想要在表单里提交的文件。下面的是示例对此进行演示,新建HTML文档如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>结合拖放、文件API和FormData对象</title>
<style>
body > * {float: left;}
#target {border:medium double black;margin: 4px;height: 75px;width: 200px;text-align: center;display: table;}
#target > p {display: table-cell;vertical-align: middle;}
table {margin: 4px;border-collapse: collapse;}
th,td {padding: 4px;}
</style>
</head>
<body>
<form id="form1" method="post" action="http://localhost:53396/ajax/html5/fileupload.aspx">
<div id="target">
<p id="msg">Drop Files Here</p>
</div>
<table id="data" border="1"></table>
<button id="submit" type="submit">Submit Form</button>
</form>
<script>
var target = document.getElementById("target");
var httpRequest;
var fileList;
document.getElementById("submit").onclick = handleButtonPress;
target.ondragenter = handleDrag;
target.ondragover = handleDrag;
function handleDrag(e){
e.preventDefault();
}
target.ondrop = function(e){
fileList = e.dataTransfer.files;
var tableElem = document.getElementById("data");
tableElem.innerHTML = "<tr><th>Name</th><th>Type</th><th>Size</th></tr>";
for(var i=0;i<fileList.length;i++){
var row = "<tr><td>"
+ fileList[i].name+"</td><td>"
+ fileList[i].type+"</td><td>"
+ fileList[i].size+"</td></tr>";
tableElem.innerHTML += row;
}
e.preventDefault();
}
function handleButtonPress(e){
e.preventDefault();
var form = document.getElementById("form1");
var formData = new FormData(form);
if(fileList || true){
for(var i=;i<fileList.length;i++){
formData.append("file"+i,fileList[i]);
}
}
httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = handleResponse;
httpRequest.open("POST",form.action);
httpRequest.send(formData);
}
function handleResponse(){
if(httpRequest.readyState == 4 && httpRequest.status == 200){
alert(httpRequest.responseText);
}
}
</script>
</body>
</html>

这个例子里用FormData.append方法添加那些放入释放区的文件,并传递一个File对象作为此方法的第二个参数。当表单被提交时,文件内容会作为表单请求的一部分自动上传到服务器。

这里的服务器用.NET实现,新建Web项目,并新建 fileupload.aspx 文件,其cs代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls; namespace Web4Luka.Web.ajax.html5
{
public partial class fileupload : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string tip = "上传失败!";
string path;
if (Request.HttpMethod == "POST")
{
if (Request.ContentType.IndexOf("multipart/form-data") > -)
{
if (Request.Files.Count > )
{
for (int i = ; i < Request.Files.Count; i++)
{
HttpPostedFile file = Request.Files[i];
path = Server.MapPath("/upload/files/" + file.FileName);
if (File.Exists(path))
{
File.Delete(path);
}
file.SaveAs(path);
}
tip = "上传成功!";
}
} Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:63342");
Response.Write(tip);
}
}
}
}

注意在服务器端创建对应上传文件保存的文件夹,此例的运行效果如下图所示:

来源:《HTML5权威指南》(《The Definitive Guide to HTML5》)

【温故而知新-Javascript】使用拖放的更多相关文章

  1. JavaScript实现拖放效果

    JavaScript实现拖放效果 笔者实现该效果也是套用别人的轮子的.传送门 然后厚颜无耻的贴别人的readme~,笔者为了方便查阅就直接贴了,有不想移步的可以看这篇.不过还是最好请到原作者的GitH ...

  2. JavaScript原生拖放API入门总结

    一.背景 最早实现JavaScript拖放功能的是IE4的浏览器了.在当时,网页中只有图像和文本才能够进行拖放.IE5之后,拖放功能得到了扩展,形成了一个API(应用程序编程接口),使得几乎任何的标签 ...

  3. 深入理解javascript原生拖放

    × 目录 [1]拖放源 [2]拖放目标 [3]dataTransfer对象[4]改变光标 前面的话 拖放(drag-and-drop,DnD)其实是两个动作——拖和放.所以,它涉及到两个元素.一个是被 ...

  4. 【温故而知新-Javascript】使用地理定位

    地理定位(Geolocation)API让我们可以获取用户当前地理位置的信息(或者至少是正在运行浏览器的系统的位置).它不是HTML5规范的一部分,但经常被归组到与HTML5相关的新功能中. 1. 使 ...

  5. 【温故而知新-Javascript】使用canvas元素(第二部分)

    本文将继续介绍canvas的功能,展示如何绘制更复杂的图形(包括圆弧和曲线),如何使用剪裁区域来限制操作以及如何绘制文本.还是介绍可以应用在画布上的特效和变换,包括阴影.透明度.旋转和坐标重映射. 1 ...

  6. 【温故而知新-Javascript】使用canvas元素(第一部分)

    1. 开始使用 canvas 元素 canvas 元素非常简单,这是指它所有的功能都体现在一个JavaScript对象上,因此该元素本身只有两个属性:width 和 height. canvas 元素 ...

  7. 【温故而知新-Javascript】使用 Ajax(续)

    1. 准备向服务器发送数据 Ajax 最常见的一大用途是向服务器发送数据.最典型的情况是从 客户端发送表单数据,即用户在form元素所含的各个 input 元素里输入的值.下面代码展示了一张简单的表单 ...

  8. 【温故而知新-Javascript】使用 Ajax

    Ajax 是现代Web 应用程序开发的一项关键工具.它让你能向服务器异步发送和接收数据,然后用 Javascript 解析. Ajax 是 Asynchronous JavaScript and XM ...

  9. 【温故而知新-Javascript】使用事件

    1. 使用简单事件处理器 可以用几种不同的方式处理事件.最直接的方式是用事件属性创建一个简单事件处理器(simple event handler).元素为它们支持的每一种事件都定义了一个事件属性.举个 ...

随机推荐

  1. Pop Easy – 轻松实现模态窗口的 jQuery 插件

    PopEasy 是一款轻量的 jQuery 插件,可以帮助开发人员容易的创建效果精美的模态窗口.PopEasy 在主流浏览器中都能够正常工作,同时兼容 IE 7 哦. 您可能感兴趣的相关文章 Metr ...

  2. 高端大气上档次!10个精美的国外HTML5网站欣赏

    这篇文章挑选了10个高端大气上档次的 HTML5 网站分享给大家.作为下一代网页语言,HTML5 加入中众多的语义化标签,例如 video.audio.section.article.header.f ...

  3. CSS3里的display

    默认值:inline 适用于:所有元素 继承性:无 动画性:否 none: 隐藏对象.与visibility属性的hidden值不同,其不为被隐藏的对象保留其物理空间 inline: 指定对象为内联元 ...

  4. (转)JavaScript二:JavaScript语言的基本语法要求

    摘自:http://blog.csdn.net/erlian1992 要学习好JavaScript,首先我们要懂JavaScript语言的一些基本语法要求: 一,区分大小写 JavaScript语言区 ...

  5. JavaScript入门篇QA总结

    Q1:JS可以放在哪个位置?A1:1.放在<head>标签中,用<script type="text/javascript"></script> ...

  6. js中如何获取纯正的undefined?

    1.为什么要获取undefined? 因为undefined在javascript中不是保留字,可以被用户当做变量来赋值,这样如果我们后期需要用到undefined来检测一个变量的话,那么检测的值就不 ...

  7. SharePoint基于windows验证的如何通过组策略实现IE自动以当前域账号登录SP站点

    通过组策略实现IE自动以当前域账号登录SP站点 1. 在运行中运行MMC,启动"组策略对象编辑器". 如下图: 找到组策略如下图: 找到域 点右键编辑 找到如下图: 找到[计算机配 ...

  8. SharePoint 2013 Error - TypeError: Unable to get property 'replace' of undefined or null reference

    错误信息 TypeError: Unable to get property ‘replace’ of undefined or null referenceTypeError: Unable to ...

  9. 制作具有SSH、MySQL功能的Chroot

    由于工作需求,需要在Linux上建立SSH.MySQL两个用户. 使这两个账户连接到跳板机后仅能执行有限的命令(SSH用户只能执行SSH命令,MySQL用户只能执行MySQL命令). MySQL账户C ...

  10. [原] SharePoint 2010 WebPart与Google地图系列 一:创建显示地图的WebPart

    摘要: 作为信息化先驱的产品SharePoint 2010竟然对GIS相关技术支持如此有限,试问现在哪个企业没有大量的项目需要结合Google地图来进行开发,单纯地从Google Javascript ...