拖放API基础
drag:被拖放元素 在拖拽的过程中触发
dragend 被拖放元素 拖拽完成时
dragenter 目标元素 拖放元素进入目标元素时
dragover 目标元素 拖放元素在目标元素上时
dragleave 目标元素 拖放元素在目标元素上离开
drop 目标元素 被拖放的元素在目标元素上同时鼠标放开触发的事件
注:需要阻止dragover的默认行为才会触发drop事件
被拖拽的元素上需要设置属性:draggable="true"
,才能被拖拽
被拖拽的元素有三个事件,分别是:dragstart开始拖拽、drag正在拖拽、dragend拖拽结束(鼠标抬起)
验证一下这三个事件:
<style>
.drag {
width: 50px;
height: 50px;
background: #ccc;
border-radius: 10px;
text-align: center;
line-height: 50px;
}
.target {
width: 300px;
height: 300px;
border: 1px dashed #000;
position: absolute;
top: 0;
right: 0;
}
</style>
<div class="drag" draggable="true">drag</div>
<div class="target">target</div>
<script>
var drag = document.getElementsByClassName('drag')[0],
target = document.getElementsByClassName('target')[0];
drag.addEventListener('dragstart',function(){
console.log('dragstart');
})
drag.addEventListener('drag',function(){
console.log('drag');
})
drag.addEventListener('dragend',function(){
console.log('dragend');
})
</script>
效果:
当鼠标点击被拖拽元素时,触发dragstart事件,输出’dragstart’;
拖拽过程中,触发drag事件,输出’drag’;
鼠标抬起,拖拽结束,触发dragend事件,输出’dragend’
目标元素可绑定的事件有:dragenter,dragover,dragleave,drop
验证一下前三个
dragenter事件以鼠标位置为准,当鼠标进入目标元素范围,触发dragenter;鼠标离开目标元素范围,触发dragleave,或者鼠标抬起,被拖拽元素完全离开目标元素范围
target.addEventListener('dragenter',function(){
console.log('dragenter');
})
target.addEventListener('dragover',function(){
console.log('dragover');
})
target.addEventListener('dragleave',function(){
console.log('dragleave');
})
鼠标点在被拖拽元素最左上角时,当鼠标移入target区域,dragenter才触发,鼠标未进入,被拖拽元素进入大半部分了,也还没有触发dragenter事件;
鼠标点在被拖拽元素右边部分时,当鼠标进入target区域,被拖拽元素仅有小部分进入target范围,就已经触发了dragenter事件;
鼠标离开target区域,触发dragleave事件,或者在target区域放开鼠标,触发dragleave事件
drop事件:
在上一个例子中可以看到,鼠标在target区域放开,自动触发的是dragleave事件,被拖拽元素回到原位置,没有保留在鼠标松开的位置,这时候需要解除dragover的默认行为(即dragleave),触发drop事件,允许放置文件
target.addEventListener('dragenter',function(){
console.log('dragenter');
})
target.addEventListener('dragover',function(e){
e.preventDefault();//取消dragover的默认行为
console.log('dragover');
})
target.addEventListener('dragleave',function(){
console.log('dragleave');
})
target.addEventListener('drop',function(){
console.log('drop');
})
可以看到,鼠标在target区域松开,触发的不再是dragleave事件,而是drop
然后我就可以操作dom元素,当把元素拖到trarget范围内并drop时,让这个元素留在target中
target.addEventListener('drop',function(){
this.appendChild(drag);
//此处的this指target,是target绑定的事件,当事件触发,由target执行相应的处理函数
})
效果:
如果我有两个需要拖拽的元素,就需要判断是哪个元素被拖过来了,然后将这个元素append到target下
这时候,就需要使用dataTransfer对象了
dataTransfer对象:
1.getData()向dataTransfer对象中存放数据
2.setData() 从dataTransfer对象中读取数据
var drag = document.getElementsByClassName('drag')[0],
drag2 = document.getElementsByClassName('drag2')[0],
target = document.getElementsByClassName('target')[0];
//被拖拽元素
drag.addEventListener('dragstart',function(e){
var dt = e.dataTransfer;
dt.setData('className',this.className);//这里的this指的是drag
})
drag2.addEventListener('dragstart',function(e){
var dt = e.dataTransfer;
dt.setData('className',this.className);
})
//目标元素
target.addEventListener('dragover',function(e){
e.preventDefault();//取消dragover的默认行为
})
target.addEventListener('drop',function(e){
var dt = e.dataTransfer;
var dataName = dt.getData('className');
this.appendChild(document.getElementsByClassName(dataName)[0]);
})
效果:
拖放小demo:把元素拖到目标位置删除,效果如下:
思路:给要删除的每个元素绑定dragstart事件,把索引值传到dataTransfer对象中,drop获取到这个索引值,找到对应dom元素,删除
注意:使用for循环绑定监听时,会构成闭包,遍历的i值不会立即传到dataTransfer中,只是累加,当触发dragstart事件时,i都是5
body,
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
li {
width: 100px;
height: 50px;
background: #ccc;
text-align: center;
line-height: 50px;
margin: 10px 0;
}
.target {
width: 300px;
height: 300px;
background: black;
color: #fff;
font-size: 20px;
}
<ul>
<li class="item1" draggable="true">item1</li>
<li class="item2" draggable="true">item2</li>
<li class="item3" draggable="true">item3</li>
<li class="item4" draggable="true">item4</li>
<li class="item5" draggable="true">item5</li>
</ul>
<div class="target">删除列表</div>
<script>
var item = document.getElementsByTagName('li'),
target = document.getElementsByClassName('target')[0],
len = item.length;//每执行一次for循环,都获取一次length,耗性能;在开头获取一次,后面调用即可
//给每个被拖拽元素绑定事件
function bindListernEvent() {
for (var i = 0; i < len; i++) {
(function (j) {
item[j].addEventListener('dragstart', function (e) {
var dt = e.dataTransfer;
dt.setData('index', j);
})
}) (i)//为避免闭包带来的影响,使用立即执行函数
//每个li都绑定监听dragstart事件,只有dargstart事件发生时,才会执行后面的函数
//若for循环下直接写绑定代码,页面刷新,就开始for循环,虽然i一直增加,但绑定的函数并没有执行,因此,i直到最后,都没有真的传到dadaTransfer中
//当触发这个事件真的需要i时,每个li都传了5进去
//使用立即执行函数,每循环一次,就执行一次绑定
}
}
//给每个目标元素绑定事件,移除被拖拽元素
function bindRemoveEvent() {
target.addEventListener('dragover', function (e) {
e.preventDefault();
})
target.addEventListener('drop', function (e) {
var dt = e.dataTransfer;
var index = dt.getData('index');
console.log(index);
item[index].remove();
})
}
bindListernEvent();//为了代码的整洁,封装成函数
bindRemoveEvent();
</script>
在此基础上再增加一个小功能,当一个li覆盖到另一个li,这个li就换位置
这样每个li也是拖拽操作的目标了
注意:每次把li换位置,index也跟着变了,所以,要想获取新的index值,就需要另外寻找,不再是按顺序了
<script>
var item = document.getElementsByTagName('li'),
target = document.getElementsByClassName('target')[0],
len = item.length,
ul = document.getElementsByTagName('ul')[0];
function bindListernEvent() {
for (var i = 0; i < len; i++) {
(function(j){
item[j].addEventListener('dragstart', function (e) {
var index = findIndex(this, item);//查找当前元素在ul中的位置
var dt = e.dataTransfer;
dt.setData('index', index);
})
})(i)
item[i].addEventListener('dragover', function (e) {
e.preventDefault();
})
item[i].addEventListener('drop', function (e) {
var dt = e.dataTransfer;
var index = dt.getData('index');//第index+1个元素拖拽到当前元素上
console.log(index);
ul.insertBefore(item[index], this);//将item[index]放到当前元素的前面
})
}
}
function bindRemoveEvent() {
target.addEventListener('dragover', function (e) {
e.preventDefault();
})
target.addEventListener('drop', function (e) {
var dt = e.dataTransfer;
var index = dt.getData('index');
console.log(index);
item[index].remove();
})
}
bindListernEvent();
bindRemoveEvent();
/*
* 查找当前li元素在ul中的位置
*/
function findIndex(dom, list){
for(var i = 0; i <len; i++){
if (list[i] == dom){
return i;
}
}
}
</script>