基于 Drag 和 Drop API
一、认识
原生拖放(drag-and-drop,DnD
) 其实是两个动作—— 拖和放。所以,它涉及到两个元素。一个是 被拖的元素 ,称为 拖放源 ; 另一个是要放的目标,称为 拖放目标
二、理论
2.1 拖放源
什么样的元素才是拖放源呢?
HTML5为所有HTML元素规定了一个draggable
属性,表示元素是否可以拖动。图像和链接的draggable
属性自动被设置成了true
,而其他元素这个属性的默认值都是false
注意: 必须设置draggable='true'
才能生效,只设置draggable
不起作用
各元素可拖动情况如下:
-
文本: 文本只有在被选中的情况下才能拖动
<input value="文字可拖动">
-
图片: 图像在任何时候都可以拖动
<img alt="图像可拖动" src="https://files.cnblogs.com/files/xiaohuochai/zan.gif">
-
链接: 链接在任何时候都可以拖动
<a href="#">链接可拖动</a>
-
其他元素: 其他元素则无法被拖放, 当为元素设置
draggable
属性后,普通元素也可以拖动没有设置 draggable 不可以拖动<div id="test" style="height:30px;width:300px;background:pink;">元素不可拖动</div>
设置 draggable 才可以拖动<div draggable="true" style="height:30px;width:100px;background:pink;"></div>
2.2 拖放目标
拖放目标: 拖放目标是指被拖动的元素松开鼠标时被放置的目标
2.3 拖放源事件
拖放源事件: 拖放源涉及到3
个拖放事件。拖动拖放源时,依次触发dragstart
、drag
和dragend
这3
个事件
-
dragstart: 按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发
dragstart
事件。此时光标变成“不能放”符号(圆环中有一条反斜线),表示不能把元素放到自己上面 -
drag: 触发
dragstart
事件后,随即会触发drag
事件,而且在元素被拖动期间会持续触发该事件 -
dragend: 当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发
dragend
事件
测试代码如下所示:
<div id="test" draggable="true" style="height:30px;width:100px;background:pink;">0</div>
<script>
var timer,i=0;
test.ondragstart = function(){
this.style.backgroundColor = 'lightgreen';
}
test.ondrag = function(){
if(timer) return;
timer = setInterval(function(){
test.innerHTML = i++;
},100)
}
test.ondragend = function(){
clearInterval(timer);
timer = 0;
this.style.backgroundColor = 'pink';
}
</script>
2.4 拖放目标事件
拖放目标事件: 拖放源被拖动到拖放目标上时,将依次触发dragenter
、dragover
和dragleave
或drop
这四个事件
-
dragenter: 只要有元素被拖动到放置目标上,触发
dragenter
事件 -
dragover: 被拖动的元素在放置目标的范围内移动时,持续触发
dragover
事件 -
dragleave: 如果元素被拖出了放置目标,触发
dragleave
事件 -
drop: 如果元素被放到了放置目标中,触发
drop
事件
默认情况下,目标元素是不允许被放置的,所以不会发生drop
事件。只要在dragover
和dragenter
事件中阻止默认行为,才能成为被允许的放置目标,才能允许发生drop
事件。此时,光标变成了允许放置的符号
测试代码如下:
<div id="test" draggable="true" style="height:30px;width:130px;background:pink;float:left;">拖放源</div>
<div id="target" style="float:right;height: 200px;width:200px;background:lightblue;">拖放目标</div>
<script>
var timer,i=0;
var timer1,i1=0;
//兼容IE8-浏览器
test.onmousedown = function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(){
this.style.backgroundColor = 'lightgreen';
this.innerHTML = '开始拖动';
}
test.ondrag = function(){
if(timer) return;
timer = setInterval(function(){
test.innerHTML = '元素已被拖动' + ++i + '秒';
},1000);
}
test.ondragend = function(){
clearInterval(timer);
timer = 0;i =0;
this.innerHTML = '结束拖动';
this.style.backgroundColor = 'pink';
}
target.ondragenter = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.innerHTML = '有元素进入目标区域';
this.style.background = 'red';
}
target.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
if(timer1) return;
timer1 = setInterval(function(){
target.innerHTML = '元素已进入' + (++i1) + '秒';
},1000);
}
target.ondragleave = function(){
clearInterval(timer1);
timer1 = 0;i1=0;
this.innerHTML = '元素已离开目标区域';
this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(){
clearInterval(timer1);
timer1 = 0;i1=0;
this.innerHTML = '元素已落在目标区域';
this.style.backgroundColor = 'orange';
}
</script>
2.5 dataTransfer 对象方法
为了在拖放操作时实现数据交换,引入了dataTransfer
对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。dataTransfer
对象有两个主要方法: getData()
和setData()
- getData():
getData()
可以取得由setData()
保存的值。setData()
方法的第一个参数,也是getData()
方法唯一的一个参数,是一个字符串,表示保存的数据类型,取值为text
或URL
。注意: 保存在dataTransfer
对象中的数据只能在drop
事件处理程序中读取 o - setData(): 在拖放链接或图像时,会调用
setData()
方法并保存URL
测试代码如下:
<div id="test" draggable="true" data-value="这是一个秘密" style="height:30px;width:100px;background:pink;">拖动源</div>
<div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目标</div>
<div id="result"></div>
<script>
//兼容IE8-浏览器
test.onmousedown = function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(e){
e = e || event;
e.dataTransfer.setData('text',test.getAttribute('data-value'));
}
target.ondragenter = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.innerHTML = '有元素进入目标区域';
this.style.background = 'red';
}
target.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
target.ondragleave = function(e){
e = e || event;
this.innerHTML = '元素已离开目标区域';
this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
result.innerHTML = '落入目标区域的文字为:' + e.dataTransfer.getData('text');
this.innerHTML = '元素已落在目标区域';
this.style.backgroundColor = 'orange';
}
</script>
2.6 dataTransfer 对象属性
利用dataTransfer
对象,不仅可以传输数据,还能通过它来确定被拖动的元素以及作为放罝目标的元素能够接收什么操作。为此,需要访问dataTransfer
对象的两个属性: dropEffect
和effectAllowed
实际上,这两个属性并没有什么用,只是拖动源在拖动目标上移动时,改变不同的光标而已
dropEffect: 可以知道被拖动的元素能够执行哪种放置行为。这个属性有下列4个可能的值
-
none: 不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值(此时,将无法触发drop事件)
-
move: 应该把拖动的元素移动到放置目标
-
copy: 应该把拖动的元素复制到放置目标
-
link: 表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)
注意: 必须在ondragover
事件处理程序中针对放置目标来设置dropEffect
属性
effectAllowed: 只有搭配effectAllowed
属性才有用。effectAllowed
属性表示允许拖动元素的哪种dropEffect
。effectAllowed
属性可能的值如下:
-
uninitialized: 没有给被拖动的元素设置任何放置行为
-
none: 被拖动的元素不能有任何行为
-
copy: 只允许值为"copy"的dropEffect
-
link: 只允许值为"link"的dropEffect
-
move: 只允许值为"move"的dropEffect
-
copyLink: 允许值为"copy"和"link"的dropEffect
-
copyMove: 允许值为"copy"和"move"的dropEffect
-
linkMove: 允许值为"link"和"move"的dropEffect
-
all: 允许任意dropEffect
注意: 必须在ondragstart
事件处理程序中设置effectAllowed
属性
测试代码
<div id="test" draggable="true" style="height:30px;width:100px;background:pink;display:inline-block;">拖放源</div>
<br>
<div id="target1" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(none)拖放目标</div>
<div id="target2" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(move)拖放目标</div>
<div id="target3" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(copy)拖放目标</div>
<div id="target4" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(link)拖放目标</div>
<div id="result"></div>
<script>
//兼容IE8-浏览器
test.onmousedown =function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(e){
e = e || event;
//兼容firefox浏览器
e.dataTransfer.setData('text','');
e.dataTransfer.effectAllowed = 'all';
}
target1.ondragenter = target2.ondragenter =target3.ondragenter =target4.ondragenter =function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}this.style.background = 'red';
}
target1.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'none';
}
target2.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'move';
}
target3.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'copy';
}
target4.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'link';
}
target1.ondragleave = target2.ondragleave =target3.ondragleave =target4.ondragleave =function(e){
e = e || event; this.style.backgroundColor = 'lightblue';
}
target1.ondrop = target2.ondrop =target3.ondrop =target4.ondrop =function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.style.backgroundColor = 'orange';
}
</script>