【温故而知新-Javascript】使用拖放
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】使用拖放的更多相关文章
- JavaScript实现拖放效果
JavaScript实现拖放效果 笔者实现该效果也是套用别人的轮子的.传送门 然后厚颜无耻的贴别人的readme~,笔者为了方便查阅就直接贴了,有不想移步的可以看这篇.不过还是最好请到原作者的GitH ...
- JavaScript原生拖放API入门总结
一.背景 最早实现JavaScript拖放功能的是IE4的浏览器了.在当时,网页中只有图像和文本才能够进行拖放.IE5之后,拖放功能得到了扩展,形成了一个API(应用程序编程接口),使得几乎任何的标签 ...
- 深入理解javascript原生拖放
× 目录 [1]拖放源 [2]拖放目标 [3]dataTransfer对象[4]改变光标 前面的话 拖放(drag-and-drop,DnD)其实是两个动作——拖和放.所以,它涉及到两个元素.一个是被 ...
- 【温故而知新-Javascript】使用地理定位
地理定位(Geolocation)API让我们可以获取用户当前地理位置的信息(或者至少是正在运行浏览器的系统的位置).它不是HTML5规范的一部分,但经常被归组到与HTML5相关的新功能中. 1. 使 ...
- 【温故而知新-Javascript】使用canvas元素(第二部分)
本文将继续介绍canvas的功能,展示如何绘制更复杂的图形(包括圆弧和曲线),如何使用剪裁区域来限制操作以及如何绘制文本.还是介绍可以应用在画布上的特效和变换,包括阴影.透明度.旋转和坐标重映射. 1 ...
- 【温故而知新-Javascript】使用canvas元素(第一部分)
1. 开始使用 canvas 元素 canvas 元素非常简单,这是指它所有的功能都体现在一个JavaScript对象上,因此该元素本身只有两个属性:width 和 height. canvas 元素 ...
- 【温故而知新-Javascript】使用 Ajax(续)
1. 准备向服务器发送数据 Ajax 最常见的一大用途是向服务器发送数据.最典型的情况是从 客户端发送表单数据,即用户在form元素所含的各个 input 元素里输入的值.下面代码展示了一张简单的表单 ...
- 【温故而知新-Javascript】使用 Ajax
Ajax 是现代Web 应用程序开发的一项关键工具.它让你能向服务器异步发送和接收数据,然后用 Javascript 解析. Ajax 是 Asynchronous JavaScript and XM ...
- 【温故而知新-Javascript】使用事件
1. 使用简单事件处理器 可以用几种不同的方式处理事件.最直接的方式是用事件属性创建一个简单事件处理器(simple event handler).元素为它们支持的每一种事件都定义了一个事件属性.举个 ...
随机推荐
- ScrollMe – 在网页中加入各种滚动动画效果
ScrollMe 是一款 jQuery 插件,用于给网页添加简单的滚动效果.当你向下滚动页面的时候,ScrollMe 可以缩放,旋转和平移页面上的元素.它易于设置,不需要任何自定义的 JavaScri ...
- DIV+CSS+JS基础+正则表达式
...............HTML系列.................... DIV元素是用来为HTML文档内大块(block-level)的内容提供结构和背景的元素.DIV的起始 ...
- 提高CSS文件可维护性的五种方法
当完成一项前端的工作之后,许多人都会忘记该项目的结构与细节.然而代码并不是马上就能完全定型,在余下的时间里还有不断的维护工作,而这些工作也许不会是你自己完成.所以,结构优良的代码能很大程度上优化它的可 ...
- cell重用的几种方式
1.使用xib重用 //ios6 之后推荐大家使用的重用方式 //动态的使用self获得当前类名,来作为唯一的标示 NSString * identifier = NSStringFromClass( ...
- SAP中禁止特定用户更改密码
在SAP管理中,有时一些账号因为是提供给大家作查询用的,受密码强度策略限制,密码不能为空.故密码设为通用后在公司内发布,为避免有些用户更改后造成其他用户无法登陆,我们可在使用TC-SU01,在登录数据 ...
- Autodesk Cloud Accelerator Program 开始报名
如果你没有注意到这个消息,那你就会错过一个前往旧金山和硅谷工程师一起工作数周的机会. 摘要一下: 时间: 1月10前提交你的提案,3月飞往旧金山 地点: 旧金山. 包住宿哦~ 不过,既然要去美国,既然 ...
- 关于AutoCAD 2014的securityload…
昨天一个朋友给我打电话提到他关于AutoCAD 2014中安全加载SECURTY LOAD的一些困惑,我的同事Fenton写了一篇博客,对AutoCAD 2014安全加载的来龙去脉做了深入详细的阐述, ...
- SQLLDR 教程
)- 总览 http://blog.csdn.net/dbanote/article/details/9153895 )- 命令行参数 http://blog.csdn.net/dbano ...
- Activity与Service进行数据交互
Android启动Service有两种方法,一种是startService,一种是bindService.生命周期如下: 执行startService时,调用者如果没有stopService,Serv ...
- Mac终端常见命令
Mac的使用 1.Finder资源管理器 Mac的硬盘不分区,只有一个根目录 2.系统偏好设置-控制面板 快捷键: 1.win下面的快捷键由ctrl换成common开头: commond+space ...