关于拖拽元素,在网上有不少例子,但都比较简单,且大多数没有彻底考虑浏览器窗口边界。
参考多方资料,本身写了个较完善的封装拖拽的方法。
先贴出代码,复制后运行看下效果,
全部理解在注释中较详细说明。javascript
<!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中定义这些事件流。css