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 draggable
新闻热点
疑难解答