祝大家新年快乐!新年新气象!

用Javascript实现网页表单鼠标拖拽

上一篇 / 下一篇  2007-05-07 08:24:32 / 个人分类:js

PHPChina 开源社区门户E.L7~'Y,ka

_m'k}-o?0

#lYwV c@gz0

;Z `4e2@\V1]0
    作者:雪山老人 译

/V_$j!}C|0    怎么用javascrīpt进行拖PHPChina 开源社区门户~Tf$c7P H*Q

i"{X+?*L+aEGQ0  本文译自:http://www.webreference.com/programming/javascrīpt/mk/column2/index.html
#^7s] FE2Y{0  所有版权归原文所有
PHPChina 开源社区门户 Co/Yu}"~/Y9bG

PHPChina 开源社区门户2T4]Lgq9Q:Pb{/^9UK

8eP6}2~7t k9C0  Javascrīpt的特点是dom的处理与网页效果,大多数情况我们只用到了这个语言的最简单的功能,比如制作图片轮播/网页的tab等等,这篇文章将向你展示如何在自己的网页上制作拖拽.PHPChina 开源社区门户4p nB.{i%\8c)g5nc

}w#^,M U3b@6Q0  有很多理由让你的网站加入拖拽功能,最简单的一个是数据重组.例如:你有一个序列的 内容让用户排序,用户需要给每个条目进行输入或者用select选择,替代前面这个方法的就是拖拽.或许你的网站也需要一个用户可以拖动的导航窗口!那么 这些效果都是很简单:因为你可以很容易的实现!

6AHD'g5w`0

EDE,|]B0  网页上实现拖拽其实也不是很复杂.第一你需要知道鼠标坐标,第二你需要知道用户鼠标点击一个网页元素并实现拖拽,最后我们要实现移动这个元素。

)\tTa!aK4?%Z0PHPChina 开源社区门户"Iu&f1R5PV/E{7N

  点击这里运行代码PHPChina 开源社区门户qsQ {D

\(c A_1K,c:w Y0获取鼠标移动信息PHPChina 开源社区门户\cw euma

Og*Bf(e$`[:L;X!z0  第一我们需要获取鼠标的坐标.我们加一个用户函数到document.onmousemove就可以了:PHPChina 开源社区门户GH5Y'n.Iv;T

PHPChina 开源社区门户7Yl0?;{/?!a

PHPChina 开源社区门户8{_(Y/B+yn

!z(H Pah.aL0document.onmousemove = mouseMove;PHPChina 开源社区门户Qxa'r Xs+R_

PHPChina 开源社区门户(Mp4bP @?+S4W

function mouseMove(ev){
ncE r3K5Q D&o!gT0 ev           = ev || window.event;
Dd.L#\ r[ Y\0 var mousePos = mouseCoords(ev);
u(|#`^4i{:d0}
PHPChina 开源社区门户/r)K$s4LWf

&q L6w RRy/m0function mouseCoords(ev){PHPChina 开源社区门户(M ]]&Yo d C)G
 if(ev.pageX || ev.pageY){
0Ni"o%r1?9ZD } SR'^0  return {x:ev.pageX, y:ev.pageY};PHPChina 开源社区门户*q ^bgv@KP+v\
 }
w)G b n?0 return {
%F`#Uj2q$MHa)?.j!j%E0  x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
1D3G M;mj~0  y:ev.clientY + document.body.scrollTop  - document.body.clientTopPHPChina 开源社区门户"B3I6J)X \4J9e
 };PHPChina 开源社区门户4l \V%C~c
}
PHPChina 开源社区门户R&`eD3Yxk{Uy

-zFjlduW P9a0  点击这里运行代码PHPChina 开源社区门户vr\!@$Y%I

PHPChina 开源社区门户#V)n!ox~"y$h(M

  你首先要声明一个evnet对象.不论何时你移动鼠标/点击/按键 等等,会对应一个event的事件.在Internet Explorer里event是全局变量,会被存储在window.event里. 在firefox中,或者其他浏览器,event事件会被相应的自定义函数获取.当我们将mouseMove函数赋值于 document.onmousemove,mouseMove会获取鼠标移动事件.

j S^$wO0

!?HJ @D @ |0  (ev = ev || window.event) 这样让ev在所有浏览器下获取了event事件,在Firefox下"||window.event"将不起作用,因为ev已经有了赋值.在MSIE下ev是空的,所以ev将设置为window.event.

Yf7h'f:_ ^G0

X@5Zp^(^m6Er'w0  因为我们在这篇文章中需要多次获取鼠标坐标,所以我们设计了mouseCoords这个函数,它只包含了一个参数,就是the event.PHPChina 开源社区门户h:b0JVWIWi

PHPChina 开源社区门户d"F r&zBh}

  我们需要运行在MSIE与Firefox为首的其他浏览器下.Firefox以 event.pageX和event.pageY来代表鼠标相应于文档左上角的位置.如果你有一个500*500的窗口,而且你的鼠标在正中间,那么 paegX和pageY将是250,当你将页面往下滚动500px,那么pageY将是750.此时pageX不变,还是250.PHPChina 开源社区门户-X BPC1L#z

']"@"|4J+G^H0  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这两个属性中,我们再加入这些到鼠标的位置中.PHPChina 开源社区门户)E4ZT.r([){/a o

PHPChina 开源社区门户M;chMqwn)t9?Q

  很幸运,这样mouseCoords函数就完成了,我们不再为坐标的事操心了.

Vo^ |'K:v w0

P];T:M)WdC Xqm7z0捕捉鼠标点击PHPChina 开源社区门户)d2yh6e$]"F`TL

PHPChina 开源社区门户 U5A)GWT

  下次我们将知道鼠标何时点击与何时放开.如果我们跳过这一步,我们在做拖拽时将永远不知道鼠标移动上面时的动作,这将是恼人的与违反直觉的.

F;BS!w_(m!h)e z4R:y u0

7oe-^3r2PdT%z0  这里有两个函数帮助我们:onmousedown与onmouseup.我们预先设 置函数来接收document.onmousemove,这样看起来很象我们会获取document.onmousedown与 document.onmouseup.但是当我们获取document.onmousedown时,我们同时获取了任何对象的点击属性如:text, images,tables等等.我们只想获取那些需要拖拽的属性,所以我们设置函数来获取我们需要移动的对象.PHPChina 开源社区门户 |B6SD YA*?

PHPChina 开源社区门户4OX{ li_d

  点击这里运行代码

'ye8O#A#T,t:Z3l0PHPChina 开源社区门户7S7]{ qcQ Z ~

动一个元素

MF%\&v H0PHPChina 开源社区门户e4Z6H3wE$~Xi

  我们知道了怎么捕捉鼠标移动与点击.剩下的就是移动元素了.首先,要确定一个明确的页面位置,css样式表要用'absolute'.设置元素绝对位置意味着我们可以用样式表的.top和.left来定位,可以用相对位置来定位了.我们将鼠标的移动全部相对页面top-left,基于这点,我们可以进行下一步了.

K2{\{fp h\0

C/XX:F\5E0  当我们定义item.style.position='absolute',所有的操作都是改变left坐标与top坐标,然后它移动了。PHPChina 开源社区门户 p1bq/A[t}q

PHPChina 开源社区门户/Yj$w"U%yX&R7o f7[

document.onmousemove = mouseMove;
qud4|Vo#nT8{s+oX0document.onmouseup   = mouseUp;
PHPChina 开源社区门户$E#Q%{3f ?E

f!EH[Bn0var dragObject  = null;PHPChina 开源社区门户w f3Imd iJ9}5|
var mouseOffset = null;

j,l'C XyKE(u S0

Q ~9E` I k%sv0function getMouseOffset(target, ev){
@q%A#Sr m L[0 ev = ev || window.event;

d8y,a8M%{8V| \6O0PHPChina 开源社区门户!G1_bR Q!T{FI

 var docPos    = getPosition(target);
],h_0AtS0 var mousePos  = mouseCoords(ev);
pk,|/{5@oD8g2vh0 return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};PHPChina 开源社区门户` F%GO:qe
}
PHPChina 开源社区门户RXX6lCz[Xm

?,A F{}heE0function getPosition(e){
7u/` lw,L!EHv0 var left = 0;PHPChina 开源社区门户*Pb6?!|3II8^
 var top  = 0;
PHPChina 开源社区门户.@j,eD y!o9DX:e

PHPChina 开源社区门户}9ut+O*Q!p W's

 while (e.offsetParent){
?#XfnR s;N#A0a0  left += e.offsetLeft;
z2ih~ {Q-f0  top  += e.offsetTop;
A-O r;a"vk-y0d0  e     = e.offsetParent;PHPChina 开源社区门户^~}}T)d9Kr
 }

;]&e7x*hThRG0

L&YsXBWC0 left += e.offsetLeft;
cJ~7Z0l0 top  += e.offsetTop;

O~m/U&]fSpC%m0PHPChina 开源社区门户1e|6eFaby@)]v

 return {x:left, y:top};PHPChina 开源社区门户 }ToS.r)q.} ^
}

2Mm-NAp;?0PHPChina 开源社区门户2x)OL} n"C%^e,^;E'K

function mouseMove(ev){PHPChina 开源社区门户*`5l] Y/I L/Y
 ev           = ev || window.event;
Y#\bdV$zr0 var mousePos = mouseCoords(ev);

i:Lj[rX!f#g0PHPChina 开源社区门户"^G A$v)@3[N'K+c8V

 if(dragObject){PHPChina 开源社区门户|;aB7I5WN\,S%t
  dragObject.style.position = 'absolute';
!_.Il@,[/p0  dragObject.style.top      = mousePos.y - mouseOffset.y;PHPChina 开源社区门户H,Q^X2OcT"C{
  dragObject.style.left     = mousePos.x - mouseOffset.x;

#A/P S pz8b}4ri0PHPChina 开源社区门户iM;_x,V5C$r3l.x

  return false;PHPChina 开源社区门户N&t;?(H,b0b _bI/C
 }PHPChina 开源社区门户$^)w&K,V z'T
}PHPChina 开源社区门户+NO(HPF
function mouseUp(){PHPChina 开源社区门户MlN.l$L$W
 dragObject = null;PHPChina 开源社区门户;M3Du7` ej [ L
}
PHPChina 开源社区门户-\ho{9r*o

8MCnCi5L4~'L}0function makeDraggable(item){PHPChina 开源社区门户k#?P!~ `0H
 if(!item) return;PHPChina 开源社区门户X&zO-eM zPI
 item.onmousedown = function(ev){PHPChina 开源社区门户+w"{Uq4[M?ch
  dragObject  = this;PHPChina 开源社区门户4a a&J7M5E&N
  mouseOffset = getMouseOffset(this, ev);
@;]'YvV U a O v0  return false;
j o'w'yb6c1iI0 }
g*SY0?G\d0}

q De7n}0PHPChina 开源社区门户QJ&?5Y~?J:E,b[

  点击这里运行代码PHPChina 开源社区门户;B{ l `.dE \

2O}QJ2l IWZa0  你会注意到这个代码几乎是前面的全集,将前面的合在一起就实现了拖拽效果了.

~@KD a5l0

^&wp3y N,k2X0  当我们点击一个item时,我们就获取了很多变量,如鼠标位置,鼠标位置自然就包含 了那个item的坐标信息了.如果我们点击了一个20*20px图像的正中间,那么鼠标的相对坐标为{x:10,y:10}.当我们点击这个图像的左上角 那么鼠标的相对坐标为{x:0,y:0}.当我们点击时,我们用这个方法取得一些鼠标与图片校对的信息.如果我们不能加载页面item,那么信息将是 document信息,会忽略了点击的item信息.PHPChina 开源社区门户\ S CF8?+]3p]6J&d

Z:@x&Ed$y1O_ v5u0  mouseOffset函数使用了另一个函数 getPosition.getPosition的作用是返回item相对页面左上角的坐标,如果我们尝试获取item.offsetLeft或者 item.style.left,那么我们将取得item相对与父级的位置,不是整个document.所有的脚本我们都是相对整个document,这 样会更好一些.

%c+L(a c@&qblTQ0PHPChina 开源社区门户)f9}E5]p4O8^

  为了完成getPosition任务,必须循环取得item的父级,我们将加载内容到item的左/上的位置.我们需要管理想要的top与left列表.PHPChina 开源社区门户 x&T(~ Xf6Nt

PHPChina 开源社区门户7KT!Gh5D)m

  自从定义了mousemove这个函数,mouseMove就会一直运行.第一我们确定item的style.position为absolute,第二我们移动item到前面定义好的位置.当mouse点击被释放,dragObject被设置为null,mouseMove将不在做任何事.PHPChina 开源社区门户Hn$MFIBX|Ph

:I2\r&t8v:s:H.p#p0Dropping an ItemPHPChina 开源社区门户8UCE9t'M ^/?u

8h i8ew S0  前面的例子目的很简单,就是拖拽item到我们希望到的地方.我们经常还有其他目的如删除item,比如我们可以将item拖到垃圾桶里,或者其他页面定义的位置.PHPChina 开源社区门户'~+X^vI E{#`

!U%Rnq*c;A)S0  很不幸,我们有一个很大的难题,当我们拖拽,item会在鼠标之下,比如mouseove,mousedown,mouseup或者其他mouse action.如果我们拖拽一个item到垃圾桶上,鼠标信息还在item上,不在垃圾桶上.PHPChina 开源社区门户#OWua3|'c n

PHPChina 开源社区门户X\ Zo[`

  怎么解决这个问题呢?有几个方法可以来解决.第一,这是以前比较推荐的,我们在移动鼠标时item会跟随鼠标,并占用了mouseover/mousemove等鼠标事件,我们不这样做,只是让item跟随着鼠标,并不占用mouseover等鼠标事件,这样会解决问题,但是这样并不好看,我们还是希望item能直接跟在mouse下.

a7rg3IR0

E Q*Zr#_WV0  另一个选择是不做item的拖拽.你可以改变鼠标指针来显示需要拖拽的item,然后放在鼠标释放的位置.这个解决方案,也是因为美学原因不予接受.

:NDJph7dX}!H0

:o3YRJ mB,|D0  最后的解决方案是,我们并不去除拖拽效果.这种方法比前两种繁杂许多,我们需要定义我们需要释放目标的列表,当鼠标释放时,手工去检查释放的位置是否是在目标列表位置上,如果在,说明是释放在目标位置上了.PHPChina 开源社区门户f U&S9C1g

mW Q M[s0/*PHPChina 开源社区门户u,XNI9y5\;p7L F.] S
All code from the previous example is needed with the exception
p!u Hmv?!i0of the mouseUp function which is replaced belowPHPChina 开源社区门户uK'tjo
*/

iA|!Mh kL#Uv'tS0

_C%x+t/g2E0var dropTargets = [];PHPChina 开源社区门户Jw%{'T*g8T$V

c WI,Lo6tw|0function addDropTarget(dropTarget){
Tr1g5fa/?7X0 dropTargets.push(dropTarget);PHPChina 开源社区门户BQ!V)D q(RRGq
}
PHPChina 开源社区门户I9ew5s({8{+cXDA

0c}*c)s5]t*XTTD0function mouseUp(ev){
u2yA/X6\;L ?O0 ev           = ev || window.event;PHPChina 开源社区门户 w.U!^8kP6F)A1Q.ts-Y
 var mousePos = mouseCoords(ev);

iB+ex&u&o0

r!Z;p!b4t._Y0 for(var i=0; i<dropTargets.length; i++){
C!["b&x!BGH([L0  var curTarget  = dropTargets[i];
UofL PG$hx1`g"a0  var targPos    = getPosition(curTarget);
$mt2JrD k#O^0  var targWidth  = parseInt(curTarget.offsetWidth);
Hjqe"aM0  var targHeight = parseInt(curTarget.offsetHeight);
PHPChina 开源社区门户J/rLQ s!Y

PHPChina 开源社区门户(W0sR$U'M

  if(
'O A7K L0L!vg7^0   (mousePos.x > targPos.x)                &&

~`g6Zln6h$g0PHPChina 开源社区门户6? \J X4X};IhfT^

   (mousePos.x < (targPos.x + targWidth))  &&PHPChina 开源社区门户 l:w7f9]|.` v
   (mousePos.y > targPos.y)                &&PHPChina 开源社区门户xn;X P.Y2uwLuE
   (mousePos.y < (targPos.y + targHeight))){
w3kE ~*Uj+q0    // dragObject was dropped onto curTarget!
!q6F7Q&|*?Kh0n`7D0  }
8_1Z7s}{NC ~3j M7]1P0 }

tt1gX"O!Wi4@0PHPChina 开源社区门户cv `a8T/HVI w

 dragObject   = null;PHPChina 开源社区门户7H$g _V7hej"?
}

'X PBj$Pw'M s8h0PHPChina 开源社区门户^nn!Wc,P5c

  点击这里运行代码PHPChina 开源社区门户4]._ILNE4c

gSi@N*^0  鼠标释放时会去取是否有drop属性,如果存在,同时鼠标指针还在drop的范围 内,执行drop操作.我们检查鼠标指针位置是否在目标范围是用(mousePos.x>targetPos.x),而且还要符合条件 (mousePos.x<(targPos.x + targWidth)).如果所有的条件符合,说明指针确实在范围内,可以执行drop指令了.

8Dj2vb3bI v-a0

:`+lz;e&NK1bk a0Pulling It All Together

v@!x7Se m k0

.V6~*gz/EZt0  最后我们拥有了所有的drag/drop的脚本片断!下一个事情是我们将创建一个DOM处理.如果你不是很熟悉,请先阅读我的Javascrīpt Primer on DOM Manipulation.PHPChina 开源社区门户'g~nZ!j0x M_4v

D;@%K ?}G0  下面的代码将创建container(容器),而且使任何一个需要drag/drop的item变成一个容器的item.代码在这个文章第二个demo的后面,它可以用户记录一个list(列表),定为一个导航窗口在左边或者右边,或者更多的函数你可以想到的.PHPChina 开源社区门户'U%xB(Z,Xf;p1d

*@o r.s ^(`y0  下一步我们将通过"假代码"让reader看到真代码,下面为推荐:PHPChina 开源社区门户m'A${|"qr"lR

PHPChina 开源社区门户-hHH'? N2{ @

  1、当document第一次载入时,创建dragHelper DIV.dragHelper将给移动的item加阴影.真实的item没有被dragged,只是用了insertBefor和appendChild来移动了,我们隐藏了dragHelper

b)^ t]M9SgJ'a,~/}d0PHPChina 开源社区门户%p ?LLt-K2F

  2、有了mouseDown与mouseUp函数.所有的操作会对应到当到iMouseDown的状态中,只有当mouse左键为按下时iMouseDown才为真,否则为假.

J:a)N+Fo0

/yc%F:rd.d0  3、我们创建了全局变量DragDrops与全局函数CreateDragContainer.DragDrops包含了一系列相对彼此的容器.任何参数(containers)将通过CreatedcragContainer进行重组与序列化,这样可以自由的移动.CreateDragContainer函数也将item进行绑定与设置属性.

Ag0Zxp0PHPChina 开源社区门户A'SN$GE-Gk%N

  4、现在我们的代码知道每个item的加入,当我们移动处mouseMove,mouseMove函数首先会设置变量target,鼠标移动在上面的item,如果这个item在容器中(checked with getAttribute):PHPChina 开源社区门户 W,s uXI h:g0_

  • 运行一小段代码来改变目标的样式.创造rollover效果
  • 检查鼠标是否没有放开,如果没有
    • 设置curTarget代表当前item
    • 记录item的当前位置,如果需要的话,我们可以将它返回
    • 克隆当前的item到dragHelper中,我们可以移动带阴影效果的item.
    • item拷贝到dragHelper后,原有的item还在鼠标指针下,我们必须删除掉dragObj,这样脚本起作用,dragObj被包含在一个容器中.
    • 抓取容器中所有的item当前坐标,高度/宽度,这样只需要记录一次,当item被drag时,每随mouse移动,每移钟就会记录成千上万次.
  • 如果没有,不需要做任何事,因为这不是一个需要移动的item

X\0[#G$T!U0  5、检查curTarget,它应该包含一个被移动的item,如果存在,进行下面操作:PHPChina 开源社区门户&dM8K"w7}:z1g3vc

  • 开始移动带有阴影的item,这个item就是前文所创建的
  • 检查每个当前容器中的container,是否鼠标已经移动到这些范围内了
    • 我们检查看一下正在拖动的item是属于哪个container
    • 放置item在一个container的某一个item之前,或者整个container之后
    • 确认item是可见的
  • 如果鼠标不在container中,确认item是不可见了.

S X7t\ha5^*G_n0  6、剩下的事就是捕捉mouseUp的事件了

/B}o:h;p-{0PHPChina 开源社区门户^Lur;o5LE&Q

实现一个拖动的全代码:PHPChina 开源社区门户x2k7dP3K)e1i/S

PHPChina 开源社区门户*}(T [nbNO

  点击这里运行代码PHPChina 开源社区门户kR]%n;JY'^

3gX\ eT2nE%Z*u0  你现在拥有了拖拽的所有东西.PHPChina 开源社区门户w#{*Dw!N4{)uqp N

PHPChina 开源社区门户t'o%S1Yg;MU!bQ@d6?

  下面的三个demo是记录事件历史.当你的鼠标在item上移动,将记录所生的事件,如果你不明白可以尝试一下鼠标的划过或者拖动,看有什么发生.

M9|&}x ]*\0

TAG: js

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

数据统计

  • 访问量: 10304
  • 日志数: 51
  • 图片数: 3
  • 书签数: 3
  • 建立时间: 2007-02-07
  • 更新时间: 2008-02-27

RSS订阅

Open Toolbar