原文链接:Drop and Drag API学习与整理
Drop和Drag是能够DOM元素拖动与释放的API。过去我们也有一个能够实现拖动与释放的流程。今天总结一下,可以看到新的API是给我们提供了很大的便利和简化了许多代码的。
拖拽的旧方法
总的是下面下面的三个流程:(js以前是没有直接拖拽的方法的,所谓的拖拽也就是把鼠标拖拽的偏移值赋值给拖动的元素,这样就好像形成一个假象,我是可以拖拽的,实际上该元素就是我们硬性规定他的新新位置的)
通过mousedown事件捕获DOM元素
通过mousemove事件来移动DOM元素
通过mouseup事件来释放DOM元素
我们在拖拽元素时,要计算该元素距离浏览器的top值和left值,当我们获得这个值后,我们再直接给他定位。获得这个top和left需要我们计算,我们很容易算出上图中红色线的距离,利用e.clientX和e.clientY,就可以获得点击该元素时,该点的位置,然后我们再算出该元素距离顶部和底部的距离,相减就可以得到鼠标距离该拖动对象的距离。
具体代码如下:
先定义一个可拖动对象,并给其父框加入对应的样式(做完以后可以试试如果main的absolute去掉会怎么样):
//CSS
.main{
position:absolute;
overflow:hidden;
width:500px;
height:500px;
border:1px solid red;
}
//DOM
<div class="main">
<img src="./face.png" alt="drag" id="item">
</div>
下面是js代码,对应解释以下几点:
- 两个对象dragObj是拖动对象,mouseOffset是鼠标距离对象的偏移量。
- 要给document加入onmousemove事件,而不是给对应的obj,否则会页面抖动。
- mouseup是用来释放对象,用来结束拖动,设置拖动对象为空。
- 设置mousePoint对象是为了使用的时候更加方便。直接以对象属性的形式访问。
- getMousePos是用来获得鼠标偏移量的。
- mousemove用来计算位置并定位。
var dragObj = null;
var mouseOffset = null;
document.onmousemove = mousemove;
document.onmouseup = mouseup;
function mouseup (e){
dragObj = null;
}
function mousePoint (x,y){
this.x = x;
this.y = y;
}
function getMousePos (e) {
var x = parseInt(e.clientX);
var y = parseInt(e.clientY);
return new mousePoint(x,y);
}
function getMouseOffset(target,e){
e = e || window.event;
var mousePos = getMousePos(e);
var x = mousePos.x - target.offsetLeft;
var y = mousePos.y - target.offsetTop;
return new mousePoint(x,y);
}
function mousemove (e){
if(!dragObj){
return;
}
e = e || window.event;
var mousePos = getMousePos(e);
if(dragObj){
console.log("entermousemove");
dragObj.style.position = "absolute";
dragObj.style.left = mousePos.x - mouseOffset.x + "px";
dragObj.style.top = mousePos.y - mouseOffset.y + "px";
return false;
}
}
function startdrag (item){
if(item){
item = document.getElementById(item);
item.onmousedown = function(e){
dragObj = this;
mouseOffset = getMouseOffset(this,e);
return false;
}
}
}
window.onload = startdrag('item');
新方法的拖拽
上面的这种方法代码比较复杂,新的API,通过dragstart和drop等添加高度抽象的事件。就能够实现更为直观的拖动与释放操作。同时,由于拖动过程中基本的显示更新处理也都是教给浏览器处理,我们开发人员就可以将精力集中于程序。
为什么已经有很丰富的拖拽库了,我们还要用Drag Drop API呢
因为在这些API中有一个叫做DataTransfer的API。它用来给拖动操作中的数据接收与传递提供支持。它并未限定数据的发送方和数据的接收方限定在一个窗口。比如:可以讲桌面上的文件拖拽到浏览器,讲DOM元素拖拽到文本编辑器。这个它消除了web应用程序与原生的应用的界限。
先煮个例子
<div id="dragElement" class="element" draggable="true"> drag me </div>
<div id="dropElement" class="element"> drop here </div>
var dragElement = document.getElementById("dragElement");
var dropElement = document.getElementById("dropElement");
dragElement.ondragstart = function(e){
e.dataTransfer.setData("text/plain",e.target.textContent);
}
dropElement.ondragover = function(e){
//zhe
if(e.dataTransfer.types == "text/plain"){
//取消浏览器默认操作(浏览器默认操作时将drop事件取消,所以这里必须取消默认操作)
e.preventDefault();
}
}
dropElement.ondrop = function(e){
//取消浏览器默认操作
e.preventDefault();
//获取所拖动时设置的数据
var dragData = e.dataTransfer.getData('text/plain');
console.log(dragData);
}
在上面这个例子中我们就获得了drag里面的值并且输出到了控制台。这个数据发送方也就是拖动的元素与接收方也就是释放的区域是一种松耦合的关系。通过对这两个区域分别实现你想要的事件处理程序就能够完成操作。
下面是具体的讲解
上面我们也看到了给drag设置了draggable=true,要让特定的元素支持被拖动,需要将元素设置为true,当然如果该元素如img,a本来默认就可以拖动,就不用写了。其他的值还可以设置成false,auto。
下面是设定拖动区域与设防区域的事件处理程序的API
能够拖动元素的事件处理程序
dragstart 在拖动操作时被触发
drag 在拖动操作中被**定期**触发
dragend 在拖动操作结束时被触发
能够设定释放区域的事件处理程序
dragenter 拖动操作中,进入DOM元素的领域时被触发
dragover 拖动操作中,处于DOM元素的领域内被定时触发
dragleave 拖动操作中,离开DOM元素领域时触发
drop 在DOM元素上释放时触发
关于DataTransfer
它的功能有:
数据的接收
数据处理方式的指定
拖动图像的设定
下面是具体的属性和方法概览:
1. setData(format,data) 以format所指定的格式添加数据(只在dragstart中有效)
2. getData(format) 以format所指定的格式获取数据(只在drop事件中有效)
3. clearData([format]) 以format所指定的格式清除数据,如果没有指定format,则清除所有数据
4. types属性 包含正在拖动数据的format的数组
5. files属性 包含正在拖动文件的File对象数组
6. setDragImage(element,x,y) 设定拖动的图像(在dragstart中有效)
7. adddElement(element) 设定拖动的图像(在dragstart中有效)
8. effectAllowed 设定允许用于拖动操作的目标效果。通常会在dragstart事件中设定
9. dropEffect 设定操作的目标的效果,或由用户选择的效果。系统会在copy,move,link,none中选择。并根据院中的效果显示相应的拖动对象。
上面setDragImage和addElement的区别是什么?
区别在于拖动图像的显示位置不同。setDragImage以拖动图像的左上角为拖动位置,并可以通过x,y坐标来调整位置。而addElement则将整个容器作为拖动对象。
<div id="container" draggable="true">
<div id="dragElement" draggable="true">
</div>
</div>
var dragObj = document.getElementById("dragElement"),
container = document.getElementById("container");
dragObj.ondragstart = function(event){
event.dataTransfer.addElement(container);
}
关于文件的drag-in–drag-out
我们先前提到了通过DataTransfer的一些优点,数据的发送方和数据的接受方并未限定于同一个窗口。
比如dataTransfer的files属性以通过拖动操作来获取桌面的程序的文件。尽管要获取的是一个文件。我们只需要现在释放区域的dragover中执行preventDefault既可以。
第一个例子(files.length)
<div id="drop">
drop here
</div>
获得释放区后,监听释放操作并做处理。
var elem = document.getElementById("drop");
elem.ondragover = function(e){
e.preventDefault();
}
elem.ondrop = function(e){
//通过files.length来判别是否拖动了对象
//也就是说一次可以拖动多个文件(File对象)
if(e.dataTransfer.files.length){
alert("你拖动了文件");
var file = e.dataTransfer.files[0];
console.log(file);
}
event.preventDefault();
}
第二个例子将文件保存至桌面
可以通过拖动操作来把文件从浏览器中保存至桌面升序的方法。可以按照下列的格式,将数据设置于dataTransfer,以实现通过拖动操作讲文件从浏览器保存至桌面程序。
event.dataTransfer.setData("DownloadURL","MIMETYPE:文件名:文件URL");
通过这个功能,就能够实现与原声应用之间的双向文件收发。使桌面环境中运行的Web应用程序的实用性得到了飞跃性的提高。
下面的是一个文件下载的实例。
<a href="http://www.example.com/foo.mp3" data-downloadurl="audio/mpeg:foo.mp3:http://example.com/foo.mp3" class="dragObj" draggable="true">download mp3</a>
<a href="http://www.example.com/bar.pdf" data-downloadurl="application/pdf:bar.pdf:http://example.com/bar.pdf">download pdf</a>
先利用querySelectorAll来获得所有的drag对象。然后监听dragstart事件。然后setData下载。
var files = document.querySelectorAll(".dragObj");
for(var i=0;i<files.length;i++){
file.addEventListener("dragstart",function(e){
e.dataTransfer.setData("DownloadURL",this.dataset.downloadurl);
},false);
}