關於拖拽元素,在網上有不少例子,但都比較簡單,且大多數沒有徹底考慮瀏覽器窗口邊界。
參考多方資料,本身寫了個較完善的封裝拖拽的方法。
先貼出代碼,複製後運行看下效果,
全部理解在註釋中較詳細說明。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