一步一步實現JS拖拽外掛程式
閱讀目錄 一、js拖拽外掛程式的原理二、根據原理實現的最基本效果三、代碼抽象與最佳化四、擴充:有效拖拽元素五、效能最佳化和總結六、jquery外掛程式化js拖拽是常見的網頁效果,本文將從零開始實現一個簡單的js外掛程式。 回到頂部一、js拖拽外掛程式的原理常見的拖拽操作是什麼樣的呢?整過過程大概有下面幾個步驟: 1、用滑鼠點擊被拖拽的元素 2、按住滑鼠不放,移動滑鼠 3、拖拽元素到一定位置,放開滑鼠 這裡的過程涉及到三個dom事件:onmousedown,onmousemove,onmouseup。所以拖拽的基本思路就是: 1、用滑鼠點擊被拖拽的元素觸發onmousedown (1)設定當前元素的可拖拽為true,表示可以拖拽 (2)記錄當前滑鼠的座標x,y (3)記錄當前元素的座標x,y 2、移動滑鼠觸發onmousemove (1)判斷元素是否可拖拽,如果是則進入步驟2,否則直接返回 (2)如果元素可拖拽,則設定元素的座標 元素的x座標 = 滑鼠移動的橫向距離+元素本來的x座標 = 滑鼠現在的x座標 - 滑鼠之前的x座標 + 元素本來的x座標 元素的y座標 = 滑鼠移動的橫向距離+元素本來的y座標 = 滑鼠現在的y座標 - 滑鼠之前的y座標 + 元素本來的y座標 3、放開滑鼠觸發onmouseup (1)將滑鼠的可拖拽狀態設定成false 回到頂部二、根據原理實現的最基本效果在實現基本的效果之前,有幾點需要說明的: 1、元素想要被拖動,它的postion屬性一定要是relative或absolute 2、通過event.clientX和event.clientY擷取滑鼠的座標 3、onmousemove是綁定在document元素上而不是拖拽元素本身,這樣能解決快速拖動造成的延遲或停止移動的問題 代碼如下:
1 var dragObj = document.getElementById("test"); 2 dragObj.style.left = "0px"; 3 dragObj.style.top = "0px"; 4 5 var mouseX, mouseY, objX, objY; 6 var dragging = false; 7 8 dragObj.onmousedown = function (event) { 9 event = event || window.event;10 11 dragging = true;12 dragObj.style.position = "relative";13 14 15 mouseX = event.clientX;16 mouseY = event.clientY;17 objX = parseInt(dragObj.style.left);18 objY = parseInt(dragObj.style.top);19 }20 21 document.onmousemove = function (event) {22 event = event || window.event;23 if (dragging) {24 25 dragObj.style.left = parseInt(event.clientX - mouseX + objX) + "px";26 dragObj.style.top = parseInt(event.clientY - mouseY + objY) + "px";27 }28 29 }30 31 document.onmouseup = function () {32 dragging = false;33 }
回到頂部三、代碼抽象與最佳化上面的代碼要做成外掛程式,要將其抽象出來,基本結構如下:
1 ; (function (window, undefined) { 2 3 function Drag(ele) {}4 5 window.Drag = Drag;6 })(window, undefined);
用自執行匿名函數將程式碼封裝起來,內部定義Drag方法並暴露到全域中,直接調用Drag,傳入被拖拽的元素。 首先對一些常用的方法進行簡單的封裝:
1 ; (function (window, undefined) { 2 var dom = { 3 //綁定事件 4 on: function (node, eventName, handler) { 5 if (node.addEventListener) { 6 node.addEventListener(eventName, handler); 7 } 8 else { 9 node.attachEvent("on" + eventName, handler);10 }11 },12 //擷取元素的樣式13 getStyle: function (node, styleName) {14 var realStyle = null;15 if (window.getComputedStyle) {16 realStyle = window.getComputedStyle(node, null)[styleName];17 }18 else if (node.currentStyle) {19 realStyle = node.currentStyle[styleName];20 }21 return realStyle;22 },23 //擷取設定元素的樣式24 setCss: function (node, css) {25 for (var key in css) {26 node.style[key] = css[key];27 }28 }29 };30 31 window.Drag = Drag;32 })(window, undefined);
在一個拖拽操作中,存在著兩個對象:被拖拽的對象和滑鼠對象,我們定義了下面的兩個對象以及它們對應的操作: 首先的拖拽對象,它包含一個元素節點和拖拽之前的座標x和y:
1 function DragElement(node) { 2 this.node = node;//被拖拽的元素節點 3 this.x = 0;//拖拽之前的x座標 4 this.y = 0;//拖拽之前的y座標 5 } 6 DragElement.prototype = { 7 constructor: DragElement, 8 init: function () { 9 this.setEleCss({10 "left": dom.getStyle(node, "left"),11 "top": dom.getStyle(node, "top")12 })13 .setXY(node.style.left, node.style.top);14 },15 //設定當前的座標16 setXY: function (x, y) {17 this.x = parseInt(x) || 0;18 this.y = parseInt(y) || 0;19 return this;20 },21 //設定元素節點的樣式22 setEleCss: function (css) {23 dom.setCss(this.node, css);24 return this;25 }26 }
還有一個對象是滑鼠,它主要包含x座標和y座標:
1 function Mouse() {2 this.x = 0;3 this.y = 0;4 }5 Mouse.prototype.setXY = function (x, y) {6 this.x = parseInt(x);7 this.y = parseInt(y);8 }
這是在拖拽操作中定義的兩個對象。 如果一個頁面可以有多個拖拽元素,那應該注意什麼: 1、每個元素對應一個拖拽對象執行個體 2、每個頁面只能有一個正在拖拽中的元素 為此,我們定義了唯一一個對象用來儲存相關的配置:
1 var draggableConfig = {2 zIndex: 1,3 draggingObj: null,4 mouse: new Mouse()5 };
這個對象中有三個屬性: (1)zIndex:用來賦值給拖拽對象的zIndex屬性,有多個拖拽對象時,當兩個拖拽對象重疊時,會造成當前拖拽對象有可能被擋住,通過設定zIndex使其顯示在最頂層 (2)draggingObj:用來儲存正在拖拽的對象,在這裡去掉了前面的用來判斷是否可拖拽的變數,通過draggingObj來判斷當前是否可以拖拽以及擷取相應的拖拽對象 (3)mouse:唯一的滑鼠對象,用來儲存當前滑鼠的座標等資訊 最後是綁定onmousedown,onmouseover,onmouseout事件,整合上面的代碼如下:
1 ; (function (window, undefined) { 2 var dom = { 3 //綁定事件 4 on: function (node, eventName, handler) { 5 if (node.addEventListener) { 6 node.addEventListener(eventName, handler); 7 } 8 else { 9 node.attachEvent("on" + eventName, handler); 10 } 11 }, 12 //擷取元素的樣式 13 getStyle: function (node, styleName) { 14 var realStyle = null; 15 if (window.getComputedStyle) { 16 realStyle = window.getComputedStyle(node, null)[styleName]; 17 } 18 else if (node.currentStyle) { 19 realStyle = node.currentStyle[styleName]; 20 } 21 return realStyle; 22 }, 23 //擷取設定元素的樣式 24 setCss: function (node, css) { 25 for (var key in css) { 26 node.style[key] = css[key]; 27 } 28 } 29 }; 30 31 //#region 拖拽元素類 32 function DragElement(node) { 33 this.node = node; 34 this.x = 0; 35 this.y = 0; 36 } 37 DragElement.prototype = { 38 constructor: DragElement, 39 init: function () { 40 this.setEleCss({ 41 "left": dom.getStyle(node, "left"), 42 "top": dom.getStyle(node, "top") 43 }) 44 .setXY(node.style.left, node.style.top); 45 }, 46 setXY: function (x, y) { 47 this.x = parseInt(x) || 0; 48 this.y = parseInt(y) || 0; 49 return this; 50 }, 51 setEleCss: function (css) { 52 dom.setCss(this.node, css); 53 return this; 54 } 55 } 56 //#endregion 57 58 //#region 滑鼠元素 59 function Mouse() { 60 this.x = 0; 61 this.y = 0; 62 } 63 Mouse.prototype.setXY = function (x, y) { 64 this.x = parseInt(x); 65 this.y = parseInt(y); 66 } 67 //#endregion 68 69 //拖拽配置 70 var draggableConfig = { 71 zIndex: 1, 72 draggingObj: null, 73 mouse: new Mouse() 74 }; 75 76 function Drag(ele) { 77 this.ele = ele; 78 79 function mouseDown(event) { 80 var ele = event.target || event.srcElement; 81 82 draggableConfig.mouse.setXY(event.clientX, event.clientY); 83 84 draggableConfig.draggingObj = new DragElement(ele); 85 draggableConfig.draggingObj 86 .setXY(ele.style.left, ele.style.top) 87 .setEleCss({ 88 "zIndex": draggableConfig.zIndex++, 89 "position": "relative" 90 }); 91 } 92 93 ele.onselectstart = function () { 94 //防止拖拽對象內的文字被選中 95 return false; 96 } 97 dom.on(ele, "mousedown", mouseDown); 98 } 99 100 dom.on(document, "mousemove", function (event) {101 if (draggableConfig.draggingObj) {102 var mouse = draggableConfig.mouse,103 draggingObj = draggableConfig.draggingObj;104 draggingObj.setEleCss({105 "left": parseInt(event.clientX - mouse.x + draggingObj.x) + "px",106 "top": parseInt(event.clientY - mouse.y + draggingObj.y) + "px"107 });108 }109 })110 111 dom.on(document, "mouseup", function (event) {112 draggableConfig.draggingObj = null;113 })114 115 116 window.Drag = Drag;117 })(window, undefined);
調用方法:Drag(document.getElementById("obj")); 注意的一點,為了防止選中拖拽元素中的文字,通過onselectstart事件處理常式return false來處理這個問題。