关于拖拽元素,在网上有很多例子,但都比较简单,且大多数没有完全考虑浏览器窗口边界。
参考多方资料,自己写了个较完善的封装拖拽的方法。
先贴出代码,复制后运行看下效果,
所有理解在注释中较详细说明。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#message{
height: 50px;
width: 200px;
font-size: 30px;
line-height: 50px;
margin:0 auto;
}
.draggable{
top:100px;
left: 100px;
height: 100px;
width: 100px;
background-color: red;
position: absolute;
}
</style>
</head>
<body>
<div id="message" draggable="false"></div><div class="draggable"></div>
<script type="text/javascript">
//标准事件流
var EventUtil = {
addHandler : function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type] = handler;
}
},
removeHandler: function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type] = null;
}
},
getTarget: function(event){
return event.target || event.srcElement;
},
//如果getEvent看不懂,在我下一篇博客参看
getEvent: function(event){
var e = event || window.event;
if(!e){
var c = this.getEvent.caller;
while(c){
e = c.arguments[0];
if(e && Event==e.constructor){
break;
}
c = c.getEvent.caller;
}
}
return e;
},
preventDefault: function(event){
if(event.preventDefault()){
event.preventDefault();
}else{
event.returnValue = false;
}
}
};
function EventTarget(){
//handlers是一个对象
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
//初始化handlers的event.type属性是一个数组,并把函数成为该数组元素
addHandler: function(type,handler){
//注意这里使用this
if(typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
//执行函数,在下面调用,如果event.type属性(这是一个数组)存入了函数作为元素,取出数组中元素依次运行
run: function(event){
if(!event.target){
event.target = this;
}
if(this.handlers[event.type] instanceof Array){
var runs = this.handlers[event.type];
for(var i=0,len=runs.length;i<len;i++){
runs[i](event);
}
}
},
//遍历某event.type属性的每个元素,找到要删除的方法后删除该元素
removeHandler: function(type,handler){
if(this.handlers[type] instanceof Array){
var remove = this.handlers[type];
for(var i=0,len=remove[type].length;i<len;i++){
if(remove[i] === handler){
break;
}
}
remove.splice(i,1);
}
}
};
//准备工作做完,开始封装一个拖拽函数,返回一个拖拽对象
var dragdrop =function(){
//定义一个EventTarget对象,最后作为返回值,因为EventTarget对象在上面有add和remove接口
var dragObject = new EventTarget();
var dragging = null,diffx = 0,diffy = 0,message;
//针对ie5+,通用的获取浏览器长宽
var browerWidth = document.documentElement.clientWidth || document.body.clientWidth;
var browerHeight = document.documentElement.clientHeight || document.body.clientHeight;
//真正实现拖拽功能的函数
function handleEvent(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch(event.type){
case "mousedown":
//如果鼠标点击的标签className中有draggable,就可以拖拽
if(target.className.indexOf("draggable") > -1){
dragging = target;
//diffx是偏移量,鼠标选取位置和div最左边界的距离
diffx = event.clientX - target.offsetLeft;
diffy = event.clientY - target.offsetTop;
message = "开始拖拽吧";
dragObject.run({type:"dragstart",target:dragging,mes:message});
}
break;
case "mousemove":
if(dragging !== null){
// 获取拖拽对象的长宽
if(dragging.currentStyle){ //IE不支持getComputedStyle方法
var draggingW = dragging.currentStyle.width;
var draggingH = dragging.currentStyle.height;
}else{ //非IE浏览器可以用getComputedStyle方法
var draggingW = document.defaultView.getComputedStyle(dragging,null).width;
var draggingH = document.defaultView.getComputedStyle(dragging,null).height;
}
//注意这里使用(event.clientX-diffx)<0来判断是否越界,而不能用dragging.offsetLeft<0
//因为判断dragging.offsetLeft<0,就会在dragging.offsetLeft小于0之后再重新赋值左边距
//这就导致,浏览器显示了offsetLeft<0的div后才会再重新赋值为0
//就会得到,div一移出边界再立马紧贴边界的效果,显然很不理想
//然而直接判断event.clientX-diffx,offsetLeft会在鼠标移动中触发mousemove一直是0
if((event.clientX-diffx)<0){
//如果鼠标位置比之前要往左了,重置为0
dragging.style.left = 0 + "px";
}else if((event.clientX-diffx)>(browerWidth - parseInt(draggingW))){
//如果鼠标位置比之前要往右了,设置为最右值
dragging.style.left = (browerWidth - parseInt(draggingW)) + "px";
}else{
//正常情况直接设置值
dragging.style.left = (event.clientX - diffx) + "px";
}
if((event.clientY-diffy)<0){
dragging.style.top = 0 + "px";
}else if((event.clientY-diffy)>(browerHeight - parseInt(draggingH))){
dragging.style.top = (browerHeight - parseInt(draggingH)) + "px";
}else{
dragging.style.top = (event.clientY - diffy) + "px";
}
message = "正在拖动中";
dragObject.run({type:"drag", target: dragging, mes:message});
}
break;
case "mouseup":
message = "";
dragObject.run({type:"dragend", target: dragging, mes:message});
dragging = null;
document.onmousemove = null;
document.onmouseup = null;
break;
}
};
//外部接口
dragObject.enable = function(){
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
};
dragObject.disable = function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
};
//返回该对象用于自定义拖拽事件
return dragObject;
};
var dragObject = dragdrop();
dragObject.enable();
// 自定义拖拽事件,用于显示传入的message参数
dragObject.addHandler("dragstart", function(event){
var message = document.getElementById("message");
message.innerHTML = event.mes;
});
dragObject.addHandler("drag", function(event){
var message = document.getElementById("message");
message.innerHTML = event.mes;
});
dragObject.addHandler("dragend", function(event){
var message = document.getElementById("message");
message.innerHTML = event.mes;
});
</script>
</body>
</html>
ps:注意138行,可以试着把(event.clientX-diffx)<0换成dragging.offsetLeft<0看看效果。
对拖拽代码中的具体解释:
①事件流用于兼容各浏览器
②EventTarget方法为了返回一个,外界方便操作的接口。
③ EventTarget函数内部通过switch,给拖拽元素添加onmousedown、onmousemove、onmouseup事件。
④通过将元素设置为position:absolute;再通过top和left属性实现拖拽。
⑤考虑边界情况,在函数内部的注释已十分详细。
⑥最后的dragstart、drag、dragend是元素在拖拽开始到结束时候触发的事件。
⑦返回EventTarget,为了可以给元素拖拽事件添加操作,在 EventTarget中定义这些事件流。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。