关于js拖拽早已是老生常谈,网上一搜一大坨,但是有很多并不是很完善,或者兼容性不够,或者功能不全,并且这样的东西还是自己写的好用。我打算在(一)中主要对js拖拽功能的注意点进行罗列,力求简单;在(二)中利用(一)的拖拽去实现类似google个性化首页的拖拽模块功能。
首先贴上完整code(IE/FF/Chrome)
代码
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" > < html xmlns ="http://www.w3.org/1999/xhtml" > < head > < title ></ title > < script type ="text/javascript" > var Common = { getEvent: function () { // ie/ff if (document.all) { return window.event; } func = getEvent.caller; while (func != null ) { var arg0 = func.arguments[ 0 ]; if (arg0) { if ((arg0.constructor == Event || arg0.constructor == MouseEvent) || ( typeof (arg0) == " object " && arg0.preventDefault && arg0.stopPropagation)) { return arg0; } } func = func.caller; } return null ; }, getMousePos: function (ev) { if ( ! ev) { ev = this .getEvent(); } if (ev.pageX || ev.pageY) { return { x: ev.pageX, y: ev.pageY }; } if (document.documentElement && document.documentElement.scrollTop) { return { x: ev.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft, y: ev.clientY + document.documentElement.scrollTop - document.documentElement.clientTop }; } else if (document.body) { return { x: ev.clientX + document.body.scrollLeft - document.body.clientLeft, y: ev.clientY + document.body.scrollTop - document.body.clientTop }; } }, getItself: function (id) { return " string " == typeof id ? document.getElementById(id) : id; }, getViewportSize: { w: (window.innerWidth) ? window.innerWidth : (document.documentElement && document.documentElement.clientWidth) ? document.documentElement.clientWidth : document.body.offsetWidth, h: (window.innerHeight) ? window.innerHeight : (document.documentElement && document.documentElement.clientHeight) ? document.documentElement.clientHeight : document.body.offsetHeight }, isIE: document.all ? true : false , setOuterHtml: function (obj, html) { var Objrange = document.createRange(); obj.innerHTML = html; Objrange.selectNodeContents(obj); var frag = Objrange.extractContents(); obj.parentNode.insertBefore(frag, obj); obj.parentNode.removeChild(obj); } } // /------------------------------------------------------------------------------------------------------ var Class = { create: function () { return function () { this .init.apply( this , arguments); } } } var Drag = Class.create(); Drag.prototype = { init: function (titleBar, dragDiv, Options) { // 设置点击是否透明,默认不透明 titleBar = Common.getItself(titleBar); dragDiv = Common.getItself(dragDiv); this .dragArea = { maxLeft: 0 , maxRight: Common.getViewportSize.w - dragDiv.offsetWidth - 2 , maxTop: 0 , maxBottom: Common.getViewportSize.h - dragDiv.offsetHeight - 2 }; if (Options) { this .opacity = Options.opacity ? (isNaN(parseInt(Options.opacity)) ? 100 : parseInt(Options.opacity)) : 100 ; this .keepOrigin = Options.keepOrigin ? ((Options.keepOrigin == true || Options.keepOrigin == false ) ? Options.keepOrigin : false ) : false ; if ( this .keepOrigin) { this .opacity = 50 ; } if (Options.area) { if (Options.area.left && ! isNaN(parseInt(Options.area.left))) { this .dragArea.maxLeft = Options.area.left }; if (Options.area.right && ! isNaN(parseInt(Options.area.right))) { this .dragArea.maxRight = Options.area.right }; if (Options.area.top && ! isNaN(parseInt(Options.area.top))) { this .dragArea.maxTop = Options.area.top }; if (Options.area.bottom && ! isNaN(parseInt(Options.area.bottom))) { this .dragArea.maxBottom = Options.area.bottom }; } } else { this .opacity = 100 , this .keepOrigin = false ; } this .originDragDiv = null ; this .tmpX = 0 ; this .tmpY = 0 ; this .moveable = false ; var dragObj = this ; titleBar.onmousedown = function (e) { var ev = e || window.event || Common.getEvent(); // 只允许通过鼠标左键进行拖拽,IE鼠标左键为1 FireFox为0 if (Common.isIE && ev.button == 1 || ! Common.isIE && ev.button == 0 ) { } else { return false ; } if (dragObj.keepOrigin) { dragObj.originDragDiv = document.createElement( " div " ); dragObj.originDragDiv.style.cssText = dragDiv.style.cssText; dragObj.originDragDiv.style.width = dragDiv.offsetWidth; dragObj.originDragDiv.style.height = dragDiv.offsetHeight; dragObj.originDragDiv.innerHTML = dragDiv.innerHTML; dragDiv.parentNode.appendChild(dragObj.originDragDiv); } dragObj.moveable = true ; dragDiv.style.zIndex = dragObj.GetZindex() + 1 ; var downPos = Common.getMousePos(ev); dragObj.tmpX = downPos.x - dragDiv.offsetLeft; dragObj.tmpY = downPos.y - dragDiv.offsetTop; titleBar.style.cursor = " move " ; if (Common.isIE) { dragDiv.setCapture(); } else { window.captureEvents(Event.MOUSEMOVE); } dragObj.SetOpacity(dragDiv, dragObj.opacity); // FireFox 去除容器内拖拽图片问题 if (ev.preventDefault) { ev.preventDefault(); ev.stopPropagation(); } document.onmousemove = function (e) { if (dragObj.moveable) { var ev = e || window.event || Common.getEvent(); // IE 去除容器内拖拽图片问题 if (document.all) // IE { ev.returnValue = false ; } var movePos = Common.getMousePos(ev); dragDiv.style.left = Math.max(Math.min(movePos.x - dragObj.tmpX, dragObj.dragArea.maxRight), dragObj.dragArea.maxLeft) + " px " ; dragDiv.style.top = Math.max(Math.min(movePos.y - dragObj.tmpY, dragObj.dragArea.maxBottom), dragObj.dragArea.maxTop) + " px " ; } }; document.onmouseup = function () { if (dragObj.keepOrigin) { if (Common.isIE) { dragObj.originDragDiv.outerHTML = "" ; } else { Common.setOuterHtml(dragObj.originDragDiv, "" ); } } if (dragObj.moveable) { if (Common.isIE) { dragDiv.releaseCapture(); } else { window.releaseEvents(Event.MOUSEMOVE); } dragObj.SetOpacity(dragDiv, 100 ); titleBar.style.cursor = " default " ; dragObj.moveable = false ; dragObj.tmpX = 0 ; dragObj.tmpY = 0 ; } }; } }, SetOpacity: function (dragDiv, n) { if (Common.isIE) { dragDiv.filters.alpha.opacity = n; } else { dragDiv.style.opacity = n / 100 ; } }, GetZindex: function () { var maxZindex = 0 ; var divs = document.getElementsByTagName( " div " ); for (z = 0 ; z < divs.length; z ++ ) { maxZindex = Math.max(maxZindex, divs[z].style.zIndex); } return maxZindex; } } window.onload = function () { new Drag( " dragDiv " , " dragDiv " , { opacity: 100 , keepOrigin: true }); // , area: { left: 50, right: 500, top: 100, bottom: 400} } </ script > </ head > < body > < div id ="dragDiv" style ="position:absolute; background-color:#FFFFFF;border:solid 1px #849BCA;width:200px;left:10px;top:10px;filter:alpha(opacity=100);opacity:1;" > < table cellpadding ="0" cellspacing ="0" border ="0" style ="width:100%;border-collapse:collapse; " > < tr id ="titleBar" style ="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;" > < th align ="left" unselectable ="on" > Title </ th > </ tr > < tr style ="height:130px;padding:3px;" align ="left" valign ="top" unselectable ="on" > < td >< img src ="http://images.cnblogs.com/logo_small.gif" alt ="pic for drag" /> Content... </ td > <!----> </ tr > </ table > </ div > < div style ="position:absolute; font-family:Tahoma;border:solid 1px #849BCA; background-color:#AAAAAA;width:200px;height:100px;left:210px;top:210px;filter:alpha(opacity=100);opacity:1; z-index:999" > Are you able to cover me? </ div > </ body > </ html >
对Drag的使用:在window.onload中,必填参数是titleBar和dragDiv,后者是要拖拽的容器,前者是拖拽容器的可拖拽部位,譬如经常遇到的通过标题栏拖动整个DIV,则titleBar即为改标题栏。而{ opacity: 100, keepOrigin: true , area: { left: 50, right: 500, top: 100, bottom: 400}} 是可选参数,用于扩展功能,opacity设置透明度,keepOrigin设置拖拽过程中是否保留原来拖拽容器,area设置拖拽范围。
- 拖拽的基本原理:当mousedown时记下鼠标点击位置离拖拽容器左边沿的距离和上边沿的距离,即tmpX,tmpY;mousemove时通过定位拖拽容器的style.left/style.top,使拖拽容器进行移动,定位到哪里则由刚刚的tmpX/tmpY和当前鼠标所在位置计算得出;mouseup时,结束移动。
- “var dragObj = this;” 这句是为了在mousedown/mouseup/mousemove事件里对Drag对象的相关变量进行引用。因为在mousedown/mouseup/mousemove里的this是window.
- 当拖拽速度太快导致鼠标移出拖拽容器,而拖拽容器位置未变,用document.mousemove代替titleBar.mousemove即可。
- 设置拖拽容器可拖拽的范围,若未设置,则默认为当前窗口可视范围。Note:在设置范围的时候使用Math.max/min来处理,而不是用If语句判断,用后者的话会导致快速拖拽时未达到容许范围边沿即停止的状况。
- 当拖拽过程中,可设置是否保留原来拖拽容器,当拖拽结束,隐藏原来容器,默认不保留。
- 当拖拽时,可设置拖拽的容器是否透明及透明度多少,默认不透明。但若拖拽过程中设置保留原来拖拽容器,即keepOrigin: true,则设置透明度为50%。
- 使右键、鼠标中键等不能拖动,仅左键单击可以拖动。Note:IE鼠标左键为event.Button=1 FireFox为event.Button=0.
- 解决如果点击在图片上无法拖拽的问题:非常杯具的是IE通过ev.cancelBubble=true;ev.returnValue = false;来防止图片的事件,注意是放在document.onmousemove中,而FireFox通过ev.preventDefault();ev.stopPropagation(); 但是是放在titleBar的mousedown事件中。
- 有一种情况,当浏览器窗口不是最大化的时候,你希望当鼠标在浏览器外移动时,浏览器里的拖拽容器仍然移动,这时就要使用鼠标事件捕获,IE中相应的是dragDiv.setCapture();与dragDiv.releaseCapture(); FF中是window.captureEvents(Event.MOUSEMOVE);与window.releaseEvents(Event.MOUSEMOVE) 。
- 确保每次拖拽时拖拽容器的zindex都不会被其他块元素覆盖。