本文转自: http://www.blueidea.com/tech/web/2006/3791.asp
Javascript的特点是dom的处理与网页效果,大多数情况我们只用到了这个语言的最简单的功能,比如制作图片轮播/网页的tab等等,这篇文章将向你展示如何在自己的网页上制作拖拽.
有很多理由让你的网站加入拖拽功能,最简单的一个是数据重组.例如:你有一个序列的内容让用户排序,用户需要给每个条目进行输入或者用select选择,替代前面这个方法的就是拖拽.或许你的网站也需要一个用户可以拖动的导航窗口!那么这些效果都是很简单:因为你可以很容易的实现!
网页上实现拖拽其实也不是很复杂.第一你需要知道鼠标坐标,第二你需要知道用户鼠标点击一个网页元素并实现拖拽,最后我们要实现移动这个元素.
获取鼠标移动信息
第一我们需要获取鼠标的坐标.我们加一个用户函数到document.onmousemove就可以了:
- document.onmousemove = mouseMove;
- function mouseMove(ev){
- ev = ev || window.event;
- var mousePos = mouseCoords(ev);
- }
- function mouseCoords(ev){
- if(ev.pageX || ev.pageY){
- return {x:ev.pageX, y:ev.pageY};
- }
- return {
- x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
- y:ev.clientY + document.body.scrollTop - document.body.clientTop
- };
- }
你首先要声明一个evnet对象.不论何时你移动鼠标/点击/按键等等,会对应一个event的事件.在Internet Explorer里event是全局变量,会被存储在window.event里. 在firefox中,或者其他浏览器,event事件会被相应的自定义函数获取.当我们将mouseMove函数赋值于document.onmousemove,mouseMove会获取鼠标移动事件.
(ev = ev || window.event) 这样让ev在所有浏览器下获取了event事件,在Firefox下"||window.event"将不起作用,因为ev已经有了赋值.在MSIE下ev是空的,所以ev将设置为window.event.
因为我们在这篇文章中需要多次获取鼠标坐标,所以我们设计了mouseCoords这个函数,它只包含了一个参数,就是the event.
我们需要运行在MSIE与Firefox为首的其他浏览器下.Firefox以event.pageX和event.pageY来代表鼠标相应于文档左上角的位置.如果你有一个500*500的窗口,而且你的鼠标在正中间,那么paegX和pageY将是250,当你将页面往下滚动500px,那么pageY将是750.此时pageX不变,还是250.
MSIE和这个相反,MSIE将event.clientX与event.clientY来代表鼠标与ie窗口的位置,并不是文档.当我们有一个500*500的窗口,鼠标在正中间,那么clientX与clientY也是250,如果你垂直滚动窗口到任何位置,clientY仍然是250,因为相对ie窗口并没有变化.想得到正确的结果,我们必须加入scrollLeft与scrollTop这两个相对于文档鼠标位置的属性.最后,由于MSIE并没有0,0的文档起始位置,因为通常会设置2px的边框在周围,边框的宽度包含在document.body.clientLeft与clientTop这两个属性中,我们再加入这些到鼠标的位置中.
很幸运,这样mouseCoords函数就完成了,我们不再为坐标的事操心了.
捕捉鼠标点击
下次我们将知道鼠标何时点击与何时放开.如果我们跳过这一步,我们在做拖拽时将永远不知道鼠标移动上面时的动作,这将是恼人的与违反直觉的.
这里有两个函数帮助我们:onmousedown与onmouseup.我们预先设置函数来接收document.onmousemove,这样看起来很象我们会获取document.onmousedown与document.onmouseup.但是当我们获取document.onmousedown时,我们同时获取了任何对象的点击属性如:text,images,tables等等.我们只想获取那些需要拖拽的属性,所以我们设置函数来获取我们需要移动的对象.
- <script>
- function mouseDown(ev){
- ev = ev || window.event;
- var target = ev.target || ev.srcElement;
- if(target.onmousedown || target.getAttribute('DragObj')){
- return false;
- }
- }
- function makeClickable(item){
- if(!item) return;
- item.onmousedown = function(ev){
- document.getElementById('ClickImage').value = this.name;
- }
- }
- document.onmousedown = mouseDown;
- window.onload = function(){
- makeClickable(document.getElementById('ClickImage1'));
- makeClickable(document.getElementById('ClickImage2'));
- makeClickable(document.getElementById('ClickImage3'));
- makeClickable(document.getElementById('ClickImage4'));
- }</script>
- <FIELDSET id=Demo3>
- <h3>
- Demo - Click any image
- </h3>
- <IMG id=ClickImage1
- src="/articleimg/2006/07/3791/drag_drop_spade.gif"
- name=Spade><IMG id=ClickImage2
- src="/articleimg/2006/07/3791/drag_drop_heart.gif"
- name=Heart><IMG id=ClickImage3
- src="/articleimg/2006/07/3791/drag_drop_diamond.gif"
- name=Diamond><IMG id=ClickImage4
- src="/articleimg/2006/07/3791/drag_drop_club.gif"
- name=Club>
- <BR>You clicked on: <INPUT id=ClickImage type="text"> </FIELDSET>
移动一个元素
我们知道了怎么捕捉鼠标移动与点击.剩下的就是移动元素了.首先,要确定一个明确的页面位置,css样式表要用'absolute'.设置元素绝对位置意味着我们可以用样式表的.top和.left来定位,可以用相对位置来定位了.我们将鼠标的移动全部相对页面top-left,基于这点,我们可以进行下一步了.
当我们定义item.style.position='absolute',所有的操作都是改变left坐标与top坐标,然后它移动了.
- document.onmousemove = mouseMove;
- document.onmouseup = mouseUp;
- var dragObject = null;
- var mouseOffset = null;
- function getMouseOffset(target, ev){
- ev = ev || window.event;
- var docPos = getPosition(target);
- var mousePos = mouseCoords(ev);
- return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
- }
- function getPosition(e){
- var left = 0;
- var top = 0;
- while (e.offsetParent){
- left += e.offsetLeft;
- top += e.offsetTop;
- e = e.offsetParent;
- }
- left += e.offsetLeft;
- top += e.offsetTop;
- return {x:left, y:top};
- }
- function mouseMove(ev){
- ev = ev || window.event;
- var mousePos = mouseCoords(ev);
- if(dragObject){
- dragObject.style.position = 'absolute';
- dragObject.style.top = mousePos.y - mouseOffset.y;
- dragObject.style.left = mousePos.x - mouseOffset.x;
- return false;
- }
- }
- function mouseUp(){
- dragObject = null;
- }
- function makeDraggable(item){
- if(!item) return;
- item.onmousedown = function(ev){
- dragObject = this;
- mouseOffset = getMouseOffset(this, ev);
- return false;
- }
- }
- <script>
- Number.prototype.NaN0=function(){return isNaN(this)?0:this;}
- var iMouseDown = false;
- var dragObject = null;
- var curTarget = null;
- function makeDraggable(item){
- if(!item) return;
- item.onmousedown = function(ev){
- dragObject = this;
- mouseOffset = getMouseOffset(this, ev);
- return false;
- }
- }
- function getMouseOffset(target, ev){
- ev = ev || window.event;
- var docPos = getPosition(target);
- var mousePos = mouseCoords(ev);
- return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
- }
- function getPosition(e){
- var left = 0;
- var top = 0;
- while (e.offsetParent){
- left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
- top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
- e = e.offsetParent;
- }
- left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
- top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
- return {x:left, y:top};
- }
- function mouseCoords(ev){
- if(ev.pageX || ev.pageY){
- return {x:ev.pageX, y:ev.pageY};
- }
- return {
- x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
- y:ev.clientY + document.body.scrollTop - document.body.clientTop
- };
- }
- function mouseDown(ev){
- ev = ev || window.event;
- var target = ev.target || ev.srcElement;
- if(target.onmousedown || target.getAttribute('DragObj')){
- return false;
- }
- }
- function mouseUp(ev){
- dragObject = null;
- iMouseDown = false;
- }
- function mouseMove(ev){
- ev = ev || window.event;
- /*
- We are setting target to whatever item the mouse is currently on
- Firefox uses event.target here, MSIE uses event.srcElement
- */
- var target = ev.target || ev.srcElement;
- var mousePos = mouseCoords(ev);
- if(dragObject){
- dragObject.style.position = 'absolute';
- dragObject.style.top = mousePos.y - mouseOffset.y;
- dragObject.style.left = mousePos.x - mouseOffset.x;
- }
- // track the current mouse state so we can compare against it next time
- lMouseState = iMouseDown;
- // this prevents items on the page from being highlighted while dragging
- if(curTarget || dragObject) return false;
- }
- document.onmousemove = mouseMove;
- document.onmousedown = mouseDown;
- document.onmouseup = mouseUp;
- window.onload = function() {
- makeDraggable(document.getElementById('DragImage5'));
- makeDraggable(document.getElementById('DragImage6'));
- makeDraggable(document.getElementById('DragImage7'));
- makeDraggable(document.getElementById('DragImage8'));
- }
- </script>
- <FIELDSET id=Demo5 style="HEIGHT: 70px"><h3>Demo - Drag any of the
- images</h3><IMG id=DragImage5
- src="/articleimg/2006/07/3791/drag_drop_spade.gif"><IMG
- id=DragImage6
- src="/articleimg/2006/07/3791/drag_drop_heart.gif"><IMG
- id=DragImage7
- src="/articleimg/2006/07/3791/drag_drop_diamond.gif"><IMG
- id=DragImage8
- src="/articleimg/2006/07/3791/drag_drop_club.gif"> </FIELDSET>
你会注意到这个代码几乎是前面的全集,将前面的合在一起就实现了拖拽效果了.
当我们点击一个item时,我们就获取了很多变量,如鼠标位置,鼠标位置自然就包含了那个item的坐标信息了.如果我们点击了一个20*20px图像的正中间,那么鼠标的相对坐标为{x:10,y:10}.当我们点击这个图像的左上角那么鼠标的相对坐标为{x:0,y:0}.当我们点击时,我们用这个方法取得一些鼠标与图片校对的信息.如果我们不能加载页面item,那么信息将是document信息,会忽略了点击的item信息.
mouseOffset函数使用了另一个函数getPosition.getPosition的作用是返回item相对页面左上角的坐标,如果我们尝试获取item.offsetLeft或者item.style.left,那么我们将取得item相对与父级的位置,不是整个document.所有的脚本我们都是相对整个document,这样会更好一些.
为了完成getPosition任务,必须循环取得item的父级,我们将加载内容到item的左/上的位置.我们需要管理想要的top与left列表.
自从定义了mousemove这个函数,mouseMove就会一直运行.第一我们确定item的style.position为absolute,第二我们移动item到前面定义好的位置.当mouse点击被释放,dragObject被设置为null,mouseMove将不在做任何事.
Dropping an Item
前面的例子目的很简单,就是拖拽item到我们希望到的地方.我们经常还有其他目的如删除item,比如我们可以将item拖到垃圾桶里,或者其他页面定义的位置.
很不幸,我们有一个很大的难题,当我们拖拽,item会在鼠标之下,比如mouseove,mousedown,mouseup或者其他mouse action.如果我们拖拽一个item到垃圾桶上,鼠标信息还在item上,不在垃圾桶上.
怎么解决这个问题呢?有几个方法可以来解决.第一,这是以前比较推荐的,我们在移动鼠标时item会跟随鼠标,并占用了mouseover/mousemove等鼠标事件,我们不这样做,只是让item跟随着鼠标,并不占用mouseover等鼠标事件,这样会解决问题,但是这样并不好看,我们还是希望item能直接跟在mouse下.
另一个选择是不做item的拖拽.你可以改变鼠标指针来显示需要拖拽的item,然后放在鼠标释放的位置.这个解决方案,也是因为美学原因不予接受.
最后的解决方案是,我们并不去除拖拽效果.这种方法比前两种繁杂许多,我们需要定义我们需要释放目标的列表,当鼠标释放时,手工去检查释放的位置是否是在目标列表位置上,如果在,说明是释放在目标位置上了.
- /*
- All code from the previous example is needed with the exception
- of the mouseUp function which is replaced below
- */
- var dropTargets = [];
- function addDropTarget(dropTarget){
- dropTargets.push(dropTarget);
- }
- function mouseUp(ev){
- ev = ev || window.event;
- var mousePos = mouseCoords(ev);
- for(var i=0; i<dropTargets.length; i++){
- var curTarget = dropTargets[i];
- var targPos = getPosition(curTarget);
- var targWidth = parseInt(curTarget.offsetWidth);
- var targHeight = parseInt(curTarget.offsetHeight);
- if(
- (mousePos.x > targPos.x) &
- (mousePos.x < (targPos.x + targWidth)) &
- (mousePos.y > targPos.y) &
- (mousePos.y < (targPos.y + targHeight))){
- // dragObject was dropped onto curTarget!
- }
- }
- dragObject = null;
- }
鼠标释放时会去取是否有drop属性,如果存在,同时鼠标指针还在drop的范围内,执行drop操作.我们检查鼠标指针位置是否在目标范围是用(mousePos.x>targetPos.x),而且还要符合条件(mousePos.x<(targPos.x + targWidth)).如果所有的条件符合,说明指针确实在范围内,可以执行drop指令了.
Pulling It All Together
最后我们拥有了所有的drag/drop的脚本片断!下一个事情是我们将创建一个DOM处理.如果你不是很熟悉,请先阅读我的JavaScript Primer on DOM Manipulation.
下面的代码将创建container(容器),而且使任何一个需要drag/drop的item变成一个容器的item.代码在这个文章第二个demo的后面,它可以用户记录一个list(列表),定为一个导航窗口在左边或者右边,或者更多的函数你可以想到的.
下一步我们将通过"假代码"让reader看到真代码,下面为推荐:
1、当document第一次载入时,创建dragHelper DIV.dragHelper将给移动的item加阴影.真实的item没有被dragged,只是用了insertBefor和appendChild来移动了,我们隐藏了dragHelper
2、有了mouseDown与mouseUp函数.所有的操作会对应到当到iMouseDown的状态中,只有当mouse左键为按下时iMouseDown才为真,否则为假.
3、我们创建了全局变量DragDrops与全局函数CreateDragContainer.DragDrops包含了一系列相对彼此的容器.任何参数(containers)将通过CreatedcragContainer进行重组与序列化,这样可以自由的移动.CreateDragContainer函数也将item进行绑定与设置属性.
4、现在我们的代码知道每个item的加入,当我们移动处mouseMove,mouseMove函数首先会设置变量target,鼠标移动在上面的item,如果这个item在容器中(checked with getAttribute):
-
- <STYLE>LI {
- MARGIN-BOTTOM: 10px
- }
- OL {
- MARGIN-TOP: 5px
- }
- .DragContainer {
- BORDER-RIGHT: #669999 2px solid; PADDING-RIGHT: 5px; BORDER-TOP: #669999 2px solid; PADDING-LEFT: 5px; FLOAT: left; PADDING-BOTTOM: 0px; MARGIN: 3px; BORDER-LEFT: #669999 2px solid; WIDTH: 100px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 2px solid
- }
- .OverDragContainer {
- BORDER-RIGHT: #669999 2px solid; PADDING-RIGHT: 5px; BORDER-TOP: #669999 2px solid; PADDING-LEFT: 5px; FLOAT: left; PADDING-BOTTOM: 0px; MARGIN: 3px; BORDER-LEFT: #669999 2px solid; WIDTH: 100px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 2px solid
- }
- .OverDragContainer {
- BACKGROUND-COLOR: #eee
- }
- .DragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .OverDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .DragDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .miniDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .OverDragBox {
- BACKGROUND-COLOR: #ffff99
- }
- .DragDragBox {
- BACKGROUND-COLOR: #ffff99
- }
- .DragDragBox {
- FILTER: alpha(opacity=50); BACKGROUND-COLOR: #ff99cc
- }
- LEGEND {
- FONT-WEIGHT: bold; FONT-SIZE: 12px; COLOR: #666699; FONT-FAMILY: verdana, tahoma, arial
- }
- FIELDSET {
- PADDING-RIGHT: 3px; PADDING-LEFT: 3px; PADDING-BOTTOM: 3px; PADDING-TOP: 3px
- }
- .History {
- FONT-SIZE: 10px; OVERFLOW: auto; WIDTH: 100%; FONT-FAMILY: verdana, tahoma, arial; HEIGHT: 82px
- }
- #DragContainer8 {
- BORDER-RIGHT: #669999 1px solid; PADDING-RIGHT: 0px; BORDER-TOP: #669999 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 0px; BORDER-LEFT: #669999 1px solid; WIDTH: 110px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 1px solid; HEIGHT: 110px
- }
- .miniDragBox {
- FLOAT: left; MARGIN: 0px 5px 5px 0px; WIDTH: 20px; HEIGHT: 20px
- }
- pre{border:1 solid #CCC;background-color:#F8F8F0;padding:10px;}
- </STYLE>
- <script>
- // iMouseDown represents the current mouse button state: up or down
- /*
- lMouseState represents the previous mouse button state so that we can
- check for button clicks and button releases:
- if(iMouseDown && !lMouseState) // button just clicked!
- if(!iMouseDown && lMouseState) // button just released!
- */
- var mouseOffset = null;
- var iMouseDown = false;
- var lMouseState = false;
- var dragObject = null;
- // Demo 0 variables
- var DragDrops = [];
- var curTarget = null;
- var lastTarget = null;
- var dragHelper = null;
- var tempDiv = null;
- var rootParent = null;
- var rootSibling = null;
- Number.prototype.NaN0=function(){return isNaN(this)?0:this;}
- function CreateDragContainer(){
- /*
- Create a new "Container Instance" so that items from one "Set" can not
- be dragged into items from another "Set"
- */
- var cDrag = DragDrops.length;
- DragDrops[cDrag] = [];
- /*
- Each item passed to this function should be a "container". Store each
- of these items in our current container
- */
- for(var i=0; i<arguments.length; i++){
- var cObj = arguments[i];
- DragDrops[cDrag].push(cObj);
- cObj.setAttribute('DropObj', cDrag);
- /*
- Every top level item in these containers should be draggable. Do this
- by setting the DragObj attribute on each item and then later checking
- this attribute in the mouseMove function
- */
- for(var j=0; j<cObj.childNodes.length; j++){
- // Firefox puts in lots of #text nodes...skip these
- if(cObj.childNodes[j].nodeName=='#text') continue;
- cObj.childNodes[j].setAttribute('DragObj', cDrag);
- }
- }
- }
- function mouseCoords(ev){
- if(ev.pageX || ev.pageY){
- return {x:ev.pageX, y:ev.pageY};
- }
- return {
- x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
- y:ev.clientY + document.body.scrollTop - document.body.clientTop
- };
- }
- function getMouseOffset(target, ev){
- evev = ev || window.event;
- var docPos = getPosition(target);
- var mousePos = mouseCoords(ev);
- return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
- }
- function getPosition(e){
- var left = 0;
- var top = 0;
- while (e.offsetParent){
- left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
- top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
- ee = e.offsetParent;
- }
- left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
- top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
- return {x:left, y:top};
- }
- function mouseMove(ev){
- evev = ev || window.event;
- /*
- We are setting target to whatever item the mouse is currently on
- Firefox uses event.target here, MSIE uses event.srcElement
- */
- var target = ev.target || ev.srcElement;
- var mousePos = mouseCoords(ev);
- // mouseOut event - fires if the item the mouse is on has changed
- if(lastTarget && (target!==lastTarget)){
- // reset the classname for the target element
- var origClass = lastTarget.getAttribute('origClass');
- if(origClass) lastTarget.className = origClass;
- }
- /*
- dragObj is the grouping our item is in (set from the createDragContainer function).
- if the item is not in a grouping we ignore it since it can't be dragged with this
- script.
- */
- var dragObj = target.getAttribute('DragObj');
- // if the mouse was moved over an element that is draggable
- if(dragObj!=null){
- // mouseOver event - Change the item's class if necessary
- if(target!=lastTarget){
- var oClass = target.getAttribute('overClass');
- if(oClass){
- target.setAttribute('origClass', target.className);
- target.className = oClass;
- }
- }
- // if the user is just starting to drag the element
- if(iMouseDown && !lMouseState){
- // mouseDown target
- curTarget = target;
- // Record the mouse x and y offset for the element
- rootParent = curTarget.parentNode;
- rootSibling = curTarget.nextSibling;
- mouseOffset = getMouseOffset(target, ev);
- // We remove anything that is in our dragHelper DIV so we can put a new item in it.
- for(var i=0; i<dragHelper.childNodes.length; i++) dragHelper.removeChild(dragHelper.childNodes[i]);
- // Make a copy of the current item and put it in our drag helper.
- dragHelper.appendChild(curTarget.cloneNode(true));
- dragHelper.style.display = 'block';
- // set the class on our helper DIV if necessary
- var dragClass = curTarget.getAttribute('dragClass');
- if(dragClass){
- dragHelper.firstChild.className = dragClass;
- }
- // disable dragging from our helper DIV (it's already being dragged)
- dragHelper.firstChild.removeAttribute('DragObj');
- /*
- Record the current position of all drag/drop targets related
- to the element. We do this here so that we do not have to do
- it on the general mouse move event which fires when the mouse
- moves even 1 pixel. If we don't do this here the script
- would run much slower.
- */
- var dragConts = DragDrops[dragObj];
- /*
- first record the width/height of our drag item. Then hide it since
- it is going to (potentially) be moved out of its parent.
- */
- curTarget.setAttribute('startWidth', parseInt(curTarget.offsetWidth));
- curTarget.setAttribute('startHeight', parseInt(curTarget.offsetHeight));
- curTarget.style.display = 'none';
- // loop through each possible drop container
- for(var i=0; i<dragConts.length; i++){
- with(dragConts[i]){
- var pos = getPosition(dragConts[i]);
- /*
- save the width, height and position of each container.
- Even though we are saving the width and height of each
- container back to the container this is much faster because
- we are saving the number and do not have to run through
- any calculations again. Also, offsetHeight and offsetWidth
- are both fairly slow. You would never normally notice any
- performance hit from these two functions but our code is
- going to be running hundreds of times each second so every
- little bit helps!
- Note that the biggest performance gain here, by far, comes
- from not having to run through the getPosition function
- hundreds of times.
- */
- setAttribute('startWidth', parseInt(offsetWidth));
- setAttribute('startHeight', parseInt(offsetHeight));
- setAttribute('startLeft', pos.x);
- setAttribute('startTop', pos.y);
- }
- // loop through each child element of each container
- for(var j=0; j<dragConts[i].childNodes.length; j++){
- with(dragConts[i].childNodes[j]){
- if((nodeName=='#text') || (dragConts[i].childNodes[j]==curTarget)) continue;
- var pos = getPosition(dragConts[i].childNodes[j]);
- // save the width, height and position of each element
- setAttribute('startWidth', parseInt(offsetWidth));
- setAttribute('startHeight', parseInt(offsetHeight));
- setAttribute('startLeft', pos.x);
- setAttribute('startTop', pos.y);
- }
- }
- }
- }
- }
- // If we get in here we are dragging something
- if(curTarget){
- // move our helper div to wherever the mouse is (adjusted by mouseOffset)
- dragHelper.style.top = mousePos.y - mouseOffset.y;
- dragHelper.style.left = mousePos.x - mouseOffset.x;
- var dragConts = DragDrops[curTarget.getAttribute('DragObj')];
- var activeCont = null;
- var xPos = mousePos.x - mouseOffset.x + (parseInt(curTarget.getAttribute('startWidth')) /2);
- var yPos = mousePos.y - mouseOffset.y + (parseInt(curTarget.getAttribute('startHeight'))/2);
- // check each drop container to see if our target object is "inside" the container
- for(var i=0; i<dragConts.length; i++){
- with(dragConts[i]){
- if(((getAttribute('startLeft')) < xPos) &
- ((getAttribute('startTop')) < yPos) &
- ((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &
- ((getAttribute('startTop') + getAttribute('startHeight')) > yPos)){
- /*
- our target is inside of our container so save the container into
- the activeCont variable and then exit the loop since we no longer
- need to check the rest of the containers
- */
- activeCont = dragConts[i];
- // exit the for loop
- break;
- }
- }
- }
- // Our target object is in one of our containers. Check to see where our div belongs
- if(activeCont){
- // beforeNode will hold the first node AFTER where our div belongs
- var beforeNode = null;
- // loop through each child node (skipping text nodes).
- for(var i=activeCont.childNodes.length-1; i>=0; i--){
- with(activeCont.childNodes[i]){
- if(nodeName=='#text') continue;
- // if the current item is "After" the item being dragged
- if(
- curTarget != activeCont.childNodes[i] &
- ((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &
- ((getAttribute('startTop') + getAttribute('startHeight')) > yPos)){
- beforeNode = activeCont.childNodes[i];
- }
- }
- }
- // the item being dragged belongs before another item
- if(beforeNode){
- if(beforeNode!=curTarget.nextSibling){
- activeCont.insertBefore(curTarget, beforeNode);
- }
- // the item being dragged belongs at the end of the current container
- } else {
- if((curTarget.nextSibling) || (curTarget.parentNode!=activeCont)){
- activeCont.appendChild(curTarget);
- }
- }
- // make our drag item visible
- if(curTarget.style.display!=''){
- curTarget.style.display = '';
- }
- } else {
- // our drag item is not in a container, so hide it.
- if(curTarget.style.display!='none'){
- curTarget.style.display = 'none';
- }
- }
- }
- // track the current mouse state so we can compare against it next time
- lMouseState = iMouseDown;
- // mouseMove target
- lastTarget = target;
- // track the current mouse state so we can compare against it next time
- lMouseState = iMouseDown;
- // this helps prevent items on the page from being highlighted while dragging
- return false;
- }
- function mouseUp(ev){
- if(curTarget){
- // hide our helper object - it is no longer needed
- dragHelper.style.display = 'none';
- // if the drag item is invisible put it back where it was before moving it
- if(curTarget.style.display == 'none'){
- if(rootSibling){
- rootParent.insertBefore(curTarget, rootSibling);
- } else {
- rootParent.appendChild(curTarget);
- }
- }
- // make sure the drag item is visible
- curTarget.style.display = '';
- }
- curTarget = null;
- iMouseDown = false;
- }
- function mouseDown(){
- iMouseDown = true;
- if(lastTarget){
- return false;
- }
- }
- document.onmousemove = mouseMove;
- document.onmousedown = mouseDown;
- document.onmouseup = mouseUp;
- window.onload = function(){
- // Create our helper object that will show the item while dragging
- dragHelper = document.createElement('DIV');
- dragHelper.style.cssText = 'position:absolute;display:none;';
- CreateDragContainer(
- document.getElementById('DragContainer1'),
- document.getElementById('DragContainer2'),
- document.getElementById('DragContainer3')
- );
- document.body.appendChild(dragHelper);
- }
- </script>
- <!--the mouse over and dragging class are defined on each item-->
- <div class="DragContainer" id="DragContainer1">
- <div class="DragBox" id="Item1" overClass="OverDragBox" dragClass="DragDragBox">Item #1</div>
- <div class="DragBox" id="Item2" overClass="OverDragBox" dragClass="DragDragBox">Item #2</div>
- <div class="DragBox" id="Item3" overClass="OverDragBox" dragClass="DragDragBox">Item #3</div>
- <div class="DragBox" id="Item4" overClass="OverDragBox" dragClass="DragDragBox">Item #4</div>
- </div>
- <div class="DragContainer" id="DragContainer2">
- <div class="DragBox" id="Item5" overClass="OverDragBox" dragClass="DragDragBox">Item #5</div>
- <div class="DragBox" id="Item6" overClass="OverDragBox" dragClass="DragDragBox">Item #6</div>
- <div class="DragBox" id="Item7" overClass="OverDragBox" dragClass="DragDragBox">Item #7</div>
- <div class="DragBox" id="Item8" overClass="OverDragBox" dragClass="DragDragBox">Item #8</div>
- </div>
- <div class="DragContainer" id="DragContainer3">
- <div class="DragBox" id="Item9" overClass="OverDragBox" dragClass="DragDragBox">Item #9</div>
- <div class="DragBox" id="Item10" overClass="OverDragBox" dragClass="DragDragBox">Item #10</div>
- <div class="DragBox" id="Item11" overClass="OverDragBox" dragClass="DragDragBox">Item #11</div>
- <div class="DragBox" id="Item12" overClass="OverDragBox" dragClass="DragDragBox">Item #12</div>
- 检查鼠标是否没有放开,如果没有
- 设置curTarget代表当前item
- 记录item的当前位置,如果需要的话,我们可以将它返回
- 克隆当前的item到dragHelper中,我们可以移动带阴影效果的item.
- item拷贝到dragHelper后,原有的item还在鼠标指针下,我们必须删除掉dragObj,这样脚本起作用,dragObj被包含在一个容器中.
- 抓取容器中所有的item当前坐标,高度/宽度,这样只需要记录一次,当item被drag时,每随mouse移动,每移钟就会记录成千上万次.
- 如果没有,不需要做任何事,因为这不是一个需要移动的item
5、检查curTarget,它应该包含一个被移动的item,如果存在,进行下面操作:
- 开始移动带有阴影的item,这个item就是前文所创建的
- 检查每个当前容器中的container,是否鼠标已经移动到这些范围内了
- 我们检查看一下正在拖动的item是属于哪个container
- 放置item在一个container的某一个item之前,或者整个container之后
- 确认item是可见的
- 如果鼠标不在container中,确认item是不可见了.
6、剩下的事就是捕捉mouseUp的事件了
你现在拥有了拖拽的所有东西.
下面的三个demo是记录事件历史.当你的鼠标在item上移动,将记录所生的事件,如果你不明白可以尝试一下鼠标的划过或者拖动,看有什么发生.
- <SCRIPT
- src="/articleimg/2006/07/3791/drag_drop.js"
- type=text/javascript></SCRIPT>
- <STYLE type=text/css>LI {
- MARGIN-BOTTOM: 10px
- }
- OL {
- MARGIN-TOP: 5px
- }
- .DragContainer {
- BORDER-RIGHT: #669999 2px solid; PADDING-RIGHT: 5px; BORDER-TOP: #669999 2px solid; PADDING-LEFT: 5px; FLOAT: left; PADDING-BOTTOM: 0px; MARGIN: 3px; BORDER-LEFT: #669999 2px solid; WIDTH: 100px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 2px solid
- }
- .OverDragContainer {
- BORDER-RIGHT: #669999 2px solid; PADDING-RIGHT: 5px; BORDER-TOP: #669999 2px solid; PADDING-LEFT: 5px; FLOAT: left; PADDING-BOTTOM: 0px; MARGIN: 3px; BORDER-LEFT: #669999 2px solid; WIDTH: 100px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 2px solid
- }
- .OverDragContainer {
- BACKGROUND-COLOR: #eee
- }
- .DragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .OverDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .DragDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .miniDragBox {
- BORDER-RIGHT: #000 1px solid; PADDING-RIGHT: 2px; BORDER-TOP: #000 1px solid; PADDING-LEFT: 2px; FONT-SIZE: 10px; MARGIN-BOTTOM: 5px; PADDING-BOTTOM: 2px; BORDER-LEFT: #000 1px solid; WIDTH: 94px; CURSOR: pointer; PADDING-TOP: 2px; BORDER-BOTTOM: #000 1px solid; FONT-FAMILY: verdana, tahoma, arial; BACKGROUND-COLOR: #eee
- }
- .OverDragBox {
- BACKGROUND-COLOR: #ffff99
- }
- .DragDragBox {
- BACKGROUND-COLOR: #ffff99
- }
- .DragDragBox {
- FILTER: alpha(opacity=50); BACKGROUND-COLOR: #ff99cc
- }
- LEGEND {
- FONT-WEIGHT: bold; FONT-SIZE: 12px; COLOR: #666699; FONT-FAMILY: verdana, tahoma, arial
- }
- FIELDSET {
- PADDING-RIGHT: 3px; PADDING-LEFT: 3px; PADDING-BOTTOM: 3px; PADDING-TOP: 3px
- }
- .History {
- FONT-SIZE: 10px; OVERFLOW: auto; WIDTH: 100%; FONT-FAMILY: verdana, tahoma, arial; HEIGHT: 82px
- }
- #DragContainer8 {
- BORDER-RIGHT: #669999 1px solid; PADDING-RIGHT: 0px; BORDER-TOP: #669999 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 0px; BORDER-LEFT: #669999 1px solid; WIDTH: 110px; PADDING-TOP: 5px; BORDER-BOTTOM: #669999 1px solid; HEIGHT: 110px
- }
- .miniDragBox {
- FLOAT: left; MARGIN: 0px 5px 5px 0px; WIDTH: 20px; HEIGHT: 20px
- }
- PRE {
- BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 10px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 10px; PADDING-BOTTOM: 10px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 10px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #f8f8f0
- }
- </STYLE>
- <body>
- <FIELDSET id=Demo0>
- <H3>Demo - Drag and Drop any item</H3>
- <DIV>
- <DIV class=DragContainer id=DragContainer1 overclass="OverDragContainer"
- history="History1">
- <DIV class=DragBox id=Item1 overclass="OverDragBox"
- dragclass="DragDragBox">Item #1</DIV>
- <DIV class=DragBox id=Item2 overclass="OverDragBox"
- dragclass="DragDragBox">Item #2</DIV>
- <DIV class=DragBox id=Item3 overclass="OverDragBox"
- dragclass="DragDragBox">Item #3</DIV>
- <DIV class=DragBox id=Item4 overclass="OverDragBox"
- dragclass="DragDragBox">Item #4</DIV></DIV>
- <DIV class=DragContainer id=DragContainer2 overclass="OverDragContainer"
- history="History1">
- <DIV class=DragBox id=Item5 overclass="OverDragBox"
- dragclass="DragDragBox">Item #5</DIV>
- <DIV class=DragBox id=Item6 overclass="OverDragBox"
- dragclass="DragDragBox">Item #6</DIV>
- <DIV class=DragBox id=Item7 overclass="OverDragBox"
- dragclass="DragDragBox">Item #7</DIV>
- <DIV class=DragBox id=Item8 overclass="OverDragBox"
- dragclass="DragDragBox">Item #8</DIV></DIV>
- <DIV class=DragContainer id=DragContainer3 overclass="OverDragContainer"
- history="History1">
- <DIV class=DragBox id=Item9 overclass="OverDragBox"
- dragclass="DragDragBox">Item #9</DIV>
- <DIV class=DragBox id=Item10 overclass="OverDragBox"
- dragclass="DragDragBox">Item #10</DIV>
- <DIV class=DragBox id=Item11 overclass="OverDragBox"
- dragclass="DragDragBox">Item #11</DIV>
- <DIV class=DragBox id=Item12 overclass="OverDragBox"
- dragclass="DragDragBox">Item #12</DIV></DIV>
- <FIELDSET><LEGEND>History</LEGEND>
- <DIV class=History id=History1></DIV></FIELDSET> </DIV></FIELDSET>
- <FIELDSET id=Demo7><LEGEND>Demo - Drag and Drop any item</LEGEND>
- <DIV>
- <OL id=DragContainer7 history="History1">
- <LI id=ListItem1>Item #1
- <LI id=ListItem2>Item #2
- <LI id=ListItem3>Item #3
- <LI id=ListItem4>Item #4
- <LI id=ListItem5>Item #5 </LI></OL></DIV></FIELDSET>
- <FIELDSET id=Demo8>
- <H3>Demo - Drag and Drop any item</H3>
- <DIV id=DragContainer8 history="History1">
- <DIV class=miniDragBox id=MiniBox1>1</DIV>
- <DIV class=miniDragBox id=MiniBox2>2</DIV>
- <DIV class=miniDragBox id=MiniBox3>3</DIV>
- <DIV class=miniDragBox id=MiniBox4>4</DIV>
- <DIV class=miniDragBox id=MiniBox5>5</DIV>
- <DIV class=miniDragBox id=MiniBox6>6</DIV>
- <DIV class=miniDragBox id=MiniBox7>7</DIV>
- <DIV class=miniDragBox id=MiniBox8>8</DIV>
- <DIV class=miniDragBox id=MiniBox9>9</DIV>
- <DIV class=miniDragBox id=MiniBox10>10</DIV>
- <DIV class=miniDragBox id=MiniBox11>11</DIV>
- <DIV class=miniDragBox id=MiniBox12>12</DIV>
- <DIV class=miniDragBox id=MiniBox13>13</DIV>
- <DIV class=miniDragBox id=MiniBox14>14</DIV>
- <DIV class=miniDragBox id=MiniBox15>15</DIV>
- <DIV class=miniDragBox id=MiniBox16>16</DIV></DIV></FIELDSET>
- <DIV></DIV>
- </body>
- </html>
关于译者:
雪山老人,lamp程序员,web开发者,你可以访问sohotx.com来了解他!
关于作者:
Mark Kahn是一个web开发者与DBA.你可以访问他的主页联系他!