- Js元素拖拽功能實現
需要解決的問題
最近項目遇到了一個問題,就是用戶某個操作需要彈出一個自定義的內容輸入框,但是有個缺點,當瀏覽太大的時候沒辦法點擊確認和取消按鈕,應為這個彈出框是采用絕對定位的,取消和確認按鈕都被擋住了。
拖拽的原理及實現
首先將元素設置為絕對定位,還用到鼠標的三個事件(mousedown、mousemove和mouseup),當用戶按下鼠標觸發mousedown事件設置被拖拽的元素為拖拽對象,然后移動鼠標連續觸發mousemove事件,判斷拖拽對象不為空時重新設置拖拽對象的位置,直到松開觸發mouseup事件將拖拽對象置空。
遇到的問題
消抖問題:
剛開始沒有計算元素的坐標與點擊事件位置之間的差值,直接將鼠標移動的位置賦值給元素的位置(top和left屬性),這樣用戶會感覺到鼠標跳動了一下,並且隨着鼠標的移動,元素一直在鼠標的右下方。
這個問題的解決辦法是:在觸發mousedown的時候除了設置被拖拽的事件,也要記錄拖拽對象的位置與事件發生的位置的水平和垂直之間的差值,觸發mousemove事件的時候將拖拽對象的位置設置為事件發生的水平和垂直位置對應減去差值。
設計問題
本想嘗試着為處理事件的函數傳入一個參數(dom對象或者jQuery對象),為這個參數添加拖動事件(三個鼠標監聽事件),但是效果不好,觸發mouseup的時候有問題,后來將mouseup事件放在了document上,這樣又發現了之前未發現的問題,在任何地方都可以觸發拖動事件讓元素被拖動,這樣也不好。
源代碼:
function dragEvent:(ele){ var drag = null, diffx = 0, diffy = 0, $e = null; if($.isNull(ele)) return; if(!($e = $(ele))) return; $(document).bind("mousedown",handleEvent); $(document).bind("mousemove",handleEvent); //$(document).bind("mouseup",handleEvent); $.muphy.addEevent(document,"mouseup",handleEvent); function handleEvent(event){ event = event || window.event; var target = $e[0]; switch(event.type){ case "mousedown": if($.Nvl(target.className).indexOf("draggable") > -1){ drag = target; diffx = event.clientX - target.offsetLeft; diffy = event.clientY - target.offsetTop; } break; case "mousemove": if(drag !== null){ drag.style.left = (event.clientX - diffx) + "px"; drag.style.top = (event.clientY - diffy) + "px"; } break; case "mouseup": drag = null; break; } } }
效果好的解決方案
除了設置被拖動的元素為絕對定位之外,再為其添加一個類class=”draggable”,不需要給拖拽事件傳入元素,將拖動事件的三個鼠標監聽事件都放在document上,當觸發mousedown事件時判斷目標元素上是否有draggable類,有的話就將這個元素賦值給拖動對象,其他鼠標事件不變。
這個方式有一點需要注意,當拖動對象有內容的時候要為其設置padding屬性以便能在邊緣觸發拖動事件。
源代碼:
function dragEvent1(){ var drag = null, diffx = 0, diffy = 0; $(document).unbind("mousedown",handleEvent); $(document).unbind("mousemove",handleEvent); $(document).unbind("mouseup",handleEvent); $(document).bind("mousedown",handleEvent); $(document).bind("mousemove",handleEvent); $(document).bind("mouseup",handleEvent); function handleEvent(event){ event = event || window.event; var target = event.target || event.srcElement; switch(event.type){ case "mousedown": if($.nvl(target.className).indexOf("draggable") > -1){ drag = target; diffx = event.clientX - target.offsetLeft; diffy = event.clientY - target.offsetTop; } break; case "mousemove": if(drag !== null){ drag.style.left = (event.clientX - diffx) + "px"; drag.style.top = (event.clientY - diffy) + "px"; } break; case "mouseup": drag = null; break; } } }
為拖放添加自定義事件
為拖動事件添加三個事件:dragstart、drag和dragend,分別表示拖動開始,正在拖動,拖動結束。
源代碼:
function dragEvent() { var drag = null, ce = new muphy.costomEvent(); diffx = 0, diffy = 0; $(document).unbind("mousedown", handleEvent); $(document).unbind("mousemove", handleEvent); $(document).unbind("mouseup", handleEvent); $(document).bind("mousedown", handleEvent); $(document).bind("mousemove", handleEvent); $(document).bind("mouseup", handleEvent); function handleEvent(event) { event = event || window.event; var target = event.target || event.srcElement; switch (event.type) { case "mousedown": if ($m.nvl(target.className).indexOf("draggable") > -1) { drag = target; diffx = event.clientX - target.offsetLeft; diffy = event.clientY - target.offsetTop; event.type = "dragstart"; ce.fire(event) } break; case "mousemove": if (drag !== null) { drag.style.left = (event.clientX - diffx) + "px"; drag.style.top = (event.clientY - diffy) + "px"; event.type = "drag"; ce.fire(event) } break; case "mouseup": if (drag != null) { drag = null; event.type = "dragend"; ce.fire(event) } break; } } return ce; }
有一個小問題就是:當在某個父節點阻止了鼠標的mousedown、mousemove和mouseup事件的時候,拖放不能正常運行。
附件:
封裝的所有代碼(muphy-common.js):
(function(window,$){ var muphy = Object.create({ isNull: function(data){ if(data === null || data === undefined){ return true; } if( typeof data === "String" && data.trim() ==='' ){ return true; } if(data instanceof Array && data.length == 0){ return true; } return false; }, nvl: function(data,obj){ if(obj === 0) return 0; return data || obj || ''; }, each: function(obj,fun){ for (var key in obj) { if(fun.call(obj[key], key, obj[key]) === false){ break; } } }, addEvent: 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; } }, removeEvent: 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; } }, _dragEvent: function(ele){ var drag = null, diffx = 0, diffy = 0, $e = null; if($.isNull(ele)) return; if(!($e = $(ele))) return; $(document).bind("mousedown",handleEvent); $(document).bind("mousemove",handleEvent); //$(document).bind("mouseup",handleEvent); $.muphy.addEevent(document,"mouseup",handleEvent); function handleEvent(event){ event = event || window.event; var target = $e[0]; switch(event.type){ case "mousedown": if($m.nvl(target.className).indexOf("draggable") > -1){ drag = target; diffx = event.clientX - target.offsetLeft; diffy = event.clientY - target.offsetTop; } break; case "mousemove": if(drag !== null){ drag.style.left = (event.clientX - diffx) + "px"; drag.style.top = (event.clientY - diffy) + "px"; } break; case "mouseup": drag = null; break; } } }, dragEvent: function(){ var drag = null, ce = new muphy.costomEvent(); diffx = 0, diffy = 0; $(document).unbind("mousedown",handleEvent); $(document).unbind("mousemove",handleEvent); $(document).unbind("mouseup",handleEvent); $(document).bind("mousedown",handleEvent); $(document).bind("mousemove",handleEvent); $(document).bind("mouseup",handleEvent); function handleEvent(event){ event = event || window.event; var target = event.target || event.srcElement; switch(event.type){ case "mousedown": if($m.nvl(target.className).indexOf("draggable") > -1){ drag = target; diffx = event.clientX - target.offsetLeft; diffy = event.clientY - target.offsetTop; event.type = "dragstart"; ce.fire(event) } break; case "mousemove": if(drag !== null){ drag.style.left = (event.clientX - diffx) + "px"; drag.style.top = (event.clientY - diffy) + "px"; event.type = "drag"; ce.fire(event) } break; case "mouseup": if(drag != null){ drag = null; event.type = "dragend"; ce.fire(event) } break; } } return ce; }, _costomEvent: function(){ var handlers = {}; this.addEvent = function(type, handler){ if(typeof handlers[type] === 'undefined'){ handlers[type] = []; } handlers[type].push(handler); } this.removeEvent = function(type, handler){ if(handlers[type] instanceof Array){ muphy.each(handlers[type], function(i){ if(this === handler){ handlers[type].splice(i,1); return false; } }); } } this.fire = function(event){ if(!event.target){ event.target = this; } if(handlers[event.type] instanceof Array){ muphy.each(handlers[event.type],function(){ this(event); }) } } }, costomEvent: function(){} }); (function(muphy){ muphy.costomEvent.prototype = { constructor: muphy.costomEvent1, handlers:{}, addEvent: function(type, handler){ if(typeof this.handlers[type] === 'undefined'){ this.handlers[type] = []; } this.handlers[type].push(handler); }, removeEvent: function(type, handler){ if(this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; muphy.each(handlers, function(i){ if(this === handler){ handlers.splice(i,1); return false; } }); } }, fire: function(event){ if(!event.target){ event.target = this; } if(this.handlers[event.type] instanceof Array){ muphy.each(this.handlers[event.type],function(){ this(event); }) } } } })(muphy); window.$m = muphy; })(window,jQuery);
以下是測試代碼
<!DOCTYPE html> <html> <head> <title>拖動事件處理</title> <script type="text/javascript" src='./jquery.js'></script> <script type="text/javascript" src='./Event.js'></script> </head> <body> <div> <div id="draggable" class="draggable" style="position: relative;width: 200px;height:100px;left: 300px;top: 200px;border: 1px solid red"> <p style="border: 1px solid green"> <span>哈哈哈哈哈</span> </p> </div> </div> </body> <script type="text/javascript"> (function (window, $) { var d = $m.dragEvent(); var fun = function (e) { console.log(e.type); } d.addEvent("dragstart", fun); d.addEvent("dragend", function () { alert(123); }); d.addEvent("dragend", fun); d.removeEvent("dragend", fun); // d.ennable1("#draggable") })(window, $) </script> </html>