拖拉(drag)指的是,用户在某个对象上按下鼠标键不放,拖动它到另一个位置,然后释放鼠标键,将该对象放在那里。
第一种 使用drag
拖拉的对象有好几种,包括元素节点、图片、链接、选中的文字等等。在网页中,除了元素节点默认不可以拖拉,其他(图片、链接、选中的文字)都是可以直接拖拉的。为了让元素节点可拖拉,可以将该节点的draggable属性设为true。
<div draggable="true">
此区域可拖拉
</div>
draggable属性可用于任何元素节点,但是图片(<img>
)和链接(<a>
)不加这个属性,就可以拖拉。对于它们,用到这个属性的时候,往往是将其设为false,防止拖拉这两种元素。
注意,一旦某个元素节点的draggable属性设为true,就无法再用鼠标选中该节点内部的文字或子节点了。
当元素节点或选中的文本被拖拉时,就会持续触发拖拉事件,包括以下一些事件。
drag
:拖拉过程中,在被拖拉的节点上持续触发(相隔几百毫秒)。
dragstart
:用户开始拖拉时,在被拖拉的节点上触发,该事件的target属性是被拖拉的节点。通常应该在这个事件的监听函数中,指定拖拉的数据。
dragend
:拖拉结束时(释放鼠标键或按下 ESC 键)在被拖拉的节点上触发,该事件的target属性是被拖拉的节点。它与dragstart事件,在同一个节点上触发。不管拖拉是否跨窗口,或者中途被取消,dragend事件总是会触发的。
dragenter
:拖拉进入当前节点时,在当前节点上触发一次,该事件的target属性是当前节点。通常应该在这个事件的监听函数中,指定是否允许在当前节点放下(drop)拖拉的数据。如果当前节点没有该事件的监听函数,或者监听函数不执行任何操作,就意味着不允许在当前节点放下数据。在视觉上显示拖拉进入当前节点,也是在这个事件的监听函数中设置。
dragover
:拖拉到当前节点上方时,在当前节点上持续触发(相隔几百毫秒),该事件的target属性是当前节点。该事件与dragenter事件的区别是,dragenter事件在进入该节点时触发,然后只要没有离开这个节点,dragover事件会持续触发。
dragleave
:拖拉操作离开当前节点范围时,在当前节点上触发,该事件的target属性是当前节点。如果要在视觉上显示拖拉离开操作当前节点,就在这个事件的监听函数中设置。
drop
:被拖拉的节点或选中的文本,释放到目标节点时,在目标节点上触发。注意,如果当前节点不允许drop,即使在该节点上方松开鼠标键,也不会触发该事件。如果用户按下 ESC 键,取消这个操作,也不会触发该事件。该事件的监听函数负责取出拖拉数据,并进行相关处理。
下面的例子展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dom Drag Event</title>
<style type="text/css">
div{
width: 100px;
height: 100px;
background-color: gray;
margin-bottom: 20px;
}
#draggable {
position: absolute;
}
</style>
</head>
<body draggable="true">
<div class="dropzone" id="0">
<div id="draggable" draggable="true">
该节点可拖拉
</div>
</div>
<div class="dropzone" id="1"></div>
<div class="dropzone" id="2"></div>
<div class="dropzone" id="3"></div>
<script>
// 被拖拉节点
var dragged;
var dif = {};
document.addEventListener('dragstart', function (event) {
dif.x = event.clientX - event.target.offsetLeft;
dif.y = event.clientY - event.target.offsetTop;
// 保存被拖拉节点
dragged = event.target;
// 被拖拉节点的背景色变透明
event.target.style.opacity = 0.5;
}, false);
document.addEventListener('dragend', function (event) {
dragged.style.left = (event.clientX - dif.x) + "px";
dragged.style.top = (event.clientY - dif.y) + "px";
// 被拖拉节点的背景色恢复正常
event.target.style.opacity = '';
}, false);
document.addEventListener('dragover', function (event) {
// 防止拖拉效果被重置,允许被拖拉的节点放入目标节点
event.preventDefault();
}, false);
document.addEventListener('dragenter', function (event) {
// 目标节点的背景色变紫色
// 由于该事件会冒泡,所以要过滤节点
if (event.target.className === 'dropzone') {
event.target.style.background = 'purple';
}
}, false);
document.addEventListener('dragleave', function( event ) {
// 目标节点的背景色恢复原样
if (event.target.className === 'dropzone') {
event.target.style.background = '';
}
}, false);
document.addEventListener('drop', function( event ) {
// 防止事件默认行为(比如某些元素节点上可以打开链接),
event.preventDefault();
if (event.target.className === 'dropzone') {
// 恢复目标节点背景色
event.target.style.background = '';
// 将被拖拉节点插入目标节点
dragged.parentNode.removeChild(dragged);
event.target.appendChild( dragged );
}
}, false);
</script>
</body>
</html>
如下是实现的效果:
关于拖拉事件,有以下几个注意点。
拖拉过程只触发以上这些拖拉事件,尽管鼠标在移动,但是鼠标事件不会触发。
将文件从操作系统拖拉进浏览器,不会触发dragstart和dragend事件。
dragenter和dragover事件的监听函数,用来取出拖拉的数据(即允许放下被拖拉的元素)。由于网页的大部分区域不适合作为放下拖拉元素的目标节点,所以这两个事件的默认设置为当前节点不允许接受被拖拉的元素。如果想要在目标节点上放下的数据,首先必须阻止这两个事件的默认行为。
第二种
除开使用 drag 事件之外,还可以使用原生的 mousedown | mousemove | mouseup 事件实现拖动效果,具体的代码实现如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>拖拽</title>
<style>
.draggable{
width: 200px;
height: 200px;
background: gray;
position: absolute;
top: 0;
left: 0;
}
</style>
<script>
window.onload=function () {
let box=document.getElementsByClassName("draggable")[0];
box.onmousedown=function (event) {
let e=event||window.event;
let distX=e.clientX-this.offsetLeft;
let distY=e.clientY-this.offsetTop;
document.onmousemove=function (event) {
let e=event||window.event;
box.style.left=e.clientX-distX+'px';
box.style.top=e.clientY-distY+'px';
};
document.onmouseup=function(){
this.onmousemove=null;
}
}
};
</script>
</head>
<body>
<div class="draggable"></div>
</body>
</html>