前言:前不久看到有面试前端的牛友总结的面经里就有一道编程题是关于拖放功能的,彼时的我还啥都不太懂,虽然现在也是前端小白一枚(哈哈哈),但是今天看了一下红宝书对JS实现原生拖放功能还是有了一个初步的认识,所以为了加深记忆写在博客里以便后续复习。
拖放是一种非常流行的用户界面模式。它的概念很简单:点击某个对象,并按住鼠标按钮不放,将鼠标移动到另一个区域,然后释放鼠标按钮将对象“放”在这里。
-
利用mousedown、mousemove、mouseup实现拖放功能
拖放的基本概念很简单:创建一个绝对定位元素,点击(mousedown)这个元素,并按住鼠标按钮不放,将鼠标移动(mousemove)到另一个区域,然后释放(mouseup)鼠标按钮。
HTML代码:
<body>
<div class="draggable">drag me ~</div>
</body>
css代码:
.draggable {
width: 100px;
height: 100px;
line-height: 100px;
background: red;
position: absolute;
text-align: center;
}
Javascript代码:
//定义跨浏览器事件对象和事件处理程序
var EventUtil = {
getEvent:function(event){
return event?event:window.event;
},
getTarget:function(event){
return event.target || event.srcElement;
},
addHandler:function(ele, type, handler) {
if(ele.addEventListener){
ele.addEventListener(type,handler,false);
}
else if(ele.attachEvent){
ele.attachEvent("on"+type,handler);
}
else
ele["on"+type]=handler;
},
removeHandler:function(ele, type, handler) {
if(ele.removeEventListener){
ele.removeEventListener(type,handler,false);
}
else if(ele.detachEvent){
ele.detachEvent("on"+type,handler);
}
else
ele["on"+type]=null;
}
};
//封装一个具有拖放功能的对象
var DragDrop = function(){
var dragging=null; //用于存放被拖动元素
var diffX=0;
var diffY=0;
function handleEvent(event){
//获取事件和目标
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//确定事件类型
switch(event.type){
case "mousedown": //当mousedown 事件发生时,会检查target 的class 是否包
//含"draggable"类,如果是,那么将target 存放到dragging中。
if(target.className.indexOf("draggable")>-1){
dragging=target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
}
break;
case "mousemove":
if(dragging!==null){
//指定位置
dragging.style.left=(event.clientX-diffX)+"px";
dragging.style.top=(event.clientY-diffY)+"px";
}
break;
case "mouseup":
dragging=null;
break;
}
};
//公共接口
return{
enable : function(){
EventUtil.addHandler(document,"mousedown",handleEvent);
EventUtil.addHandler(document,"mousemove",handleEvent);
EventUtil.addHandler(document,"mouseup",handleEvent);
},
disable : function(){
EventUtil.removeHandler(document,"mousedown",handleEvent);
EventUtil.removeHandler(document,"mousemove",handleEvent);
EventUtil.removeHandler(document,"mouseup",handleEvent);
}
};
}();
//调用方法.要使用DragDrop 对象,只要在页面上包含这些代码并调用enable().拖放会自动针对所有包含
//"draggable"类的元素启用.
DragDrop.enable();
具体效果如下图所示:
-
利用HTML5实现拖放功能
通过拖放事件,可以控制拖放相关的各个方面。其中最关键的地方在于确定哪里发生了拖放事件,有些事件是在被拖动的元素上触发的,而有些事件是在放置目标上触发的。拖动某元素时,将依次触发下列事件:
- dragstart:按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发dragstart 事件
- drag:触发dragstart 事件后,随即会触发drag 事件,而且在元素被拖动期间会持续触发该事件。
- dragend:当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发dragend 事件。
- 上述三个事件的目标都是被拖动的元素,目标元素在被拖动的情况下,大多数浏览器会为正被拖动的元素创建一个半透明的副本,这个副本始终跟随着光标移动。
当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生:
- dragenter:只要有元素被拖动到放置目标上,就会触发dragenter 事件(类似于mouseover 事件)。
- dragover:紧随其后的是dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。
- dragleave:如果元素被拖出了放置目标,dragover 事件不再发生,但会触发dragleave 事件(类似于mouseout事件)。
- drop:如果元素被放到了放置目标中,则会触发drop 事件而不是dragleave 事件。
- 上述四个事件的目标都是作为放置目标的元素。
TIips:
- 默认情况下,图像、链接和文本是可以拖动的。文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。HTML5 为所有HTML 元素规定了一个draggable 属性,表示元素是否可以拖动。图像和链接的draggable 属性自动被设置成了true,而其他元素这个属性的默认值都是false。如果想让其他元素都可以被拖动,可以把元素的draggable属性设置为true。
- 如果被拖动的元素经过某些无效放置目标时,可以看到一种特殊的光标(圆环中有一条反斜线),表示不能放置。虽然所有元素都支持放置目标事件,但这些元素默认是不允许放置的,从而就不会触发drop事件,不过,你可以把任何元素变成有效的放置目标,方法是阻止dragenter 和dragover 事件的默认行为。例如,假设有一个ID 为"droptarget"的<div>元素,可以用如下代码将它变成一个放置目标。
var droptarget = document.getElementById("droptarget");
EventUtil.addHandler(droptarget, "dragover", function(event){
EventUtil.preventDefault(event);
});
EventUtil.addHandler(droptarget, "dragenter", function(event){
EventUtil.preventDefault(event);
});
在Firefox3.5+中,drop放置事件的默认行为是打开被放到放置目标上的URL(例如图像被拖放到放置目标上时,页面就会转向图像文件),而如果是把文本拖放到放置目标上,则会导致无效URL错误。因此,为了让Firefox 支持正常的拖放,需要阻止drop事件的默认行为,阻止它打开URL。
EventUtil.addHandler(droptarget, "drop", function(event){
EventUtil.preventDefault(event);
});
为了在拖放操作时实现数据交换,可以在拖放事件的事件处理程序中访问dataTransfer对象。dataTransfer对象有两个主要方法:setData()和getData()。
- setData()方法:设置拖放操作时指定的数据类型(“Text”或“URL”)和数据内容。
getData()方法:取得由setData()保存的值,参数为字符串“text”或“URL”,表示数据类型,且只能在drop事件处理程序中读取。
//设置和接收文本数据
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");
//设置和接收URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");
综合小栗子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>拖放小实验</title>
<style>
#docker{
width:300px;
height: 300px;
border:2px solid #000;
text-align: center;
margin-bottom: 10px;
color:#694EB0;
}
#nana{
border:1px solid #000;
width:250px;
height:200px;
}
#tip{
display: block;
width:240px;
font-size:20px;
border:2px solid #eee;
margin-top: 10px;
text-align: center;
}
</style>
</head>
<body>
<div id="docker">快把娜娜放进来~</div>
<img id="nana" src="nana.jpg" width="250" height="200" alt="">
<span id="tip">Tips</span>
<script>
var docker = document.getElementById("docker");
var nana = document.getElementById("nana");
var tip = document.getElementById("tip");
nana.addEventListener("dragstart", function(event){
var dataTransfer = event.dataTransfer;
dataTransfer.setData("text", event.target.id);
event.target.style.opacity = 0.5;
tip.innerHTML="start~"
});
nana.addEventListener("dragend", function(event){
event.target.style.opacity = 1;
});
docker.addEventListener("dragenter", function(event){
event.preventDefault();
event.target.style.background="#f00";
event.target.style.borderColor="green";
tip.innerHTML="come on~";
});
docker.addEventListener("dragover", function(event){
event.preventDefault();
tip.style.color="green";
});
docker.addEventListener("dragleave", function(event){
event.target.style.background="";
event.target.style.borderColor="";
tip.innerHTML="don't go~";
});
docker.addEventListener("drop", function(event){
event.preventDefault();
var id = event.dataTransfer.getData("Text");
event.target.appendChild(document.getElementById(id));
tip.innerHTML="成功捕获一只太阳女神~";
event.target.style.borderColor="red";
tip.style.color="red";
});
</script>
</body>
</html>
具体效果如下图所示:
小结
- 使用mouse事件实现拖放功能:主要是要在DragDrop对象中定义mousedown、mousemove、mouseup三种类型事件处理程序,关键是计算鼠标拖动target的位置,这里就要明确代码中diffX、diffY的含义了。它们分别指的是元素左上角和指针位置X、Y坐标之间的差值。计算它们有什么用呢?这样能保证你鼠标从哪儿开始拖动元素,只要你没有停止拖动,鼠标位置会一直在你初始点击的地方,而不会跳到元素左上角。
- 使用HTML5实现拖放功能:主要就是记住被拖动元素会经历三个事件(dragstart、drag、dragend),通常我们只需要设置dragstart和dragend,通常在dragstart设置dataTransfer的setData()方法;目标放置元素则会有四个事件(dragenter、dragover、dragleave、drop),其中dragenter、dragover和drop事件记得先要阻止它们的默认行为,再进行后续操作,在drop事件中利用dataTransfer的getData()方法获取对应元素Id,然后往目标放置元素中添加被动拖动元素即可。