动态HTML创作之八(鼠标拖放)

让我们再次回到鼠标。拖放概念是彻底地基于鼠标事件的。就像可以捕捉键盘事件一样去捕捉鼠标事件,比如“onMouseDown(按下鼠标键)”、“onMouseUp(释放鼠标键)”和“onMouseMove(移动鼠标)”等。

初始化鼠标事件

所有的鼠标事件“onMouseDown”、“onMouseUp”和“onMouseMove”都用一种方法来初始化:
function init() {//从BODY onLoad开始调用
document.onmousedown=mouseDown
document.onmousemove=mouseMove
document.onmouseup=mouseUp
if(ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}

function mouseDown(e) {
}

function mouseMove(e) {
}

function mouseUp(e) {
}
函数名可以随意取,但是不管怎样,应该取一些容易被理解的名字如“mouseDown(e)”、“mouseMove(e)”和“mouseUp(e)”。

对于Netscape来说,“e”代表内建事件对象。可以独得当前位置:

var x=e.pageX

var y=e.pageY

当Internet Explorer读取这些代码时,将忽略这些“e”。

对于Internet Explorer,窗口包含一个事件对象,可以使用“window.event”来访问它。在鼠标事件发生的地方,“window.event”对象包含代表位置的x和y值:

var x=event.x

var y=event.y

当鼠标事件发生时,IE的浏览器会特别地反应出值,但是这并不真正反应实际文档的情况,当向下滚屏时,“window.event.y”的值并不与文档同步增加,因些在设计时不得不考虑进行调整,可以使用“document.body.scrollTop”来为滚屏文档加上数值。

var x=event.x+document.body.scrollLeft

var y=event.y+document.body.scrollTop

然后,可以把一小块代码加入来适应不同的浏览器:

if(ns4) {var x=e.pageX;var y=e.pageY}

if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}

这些代码可以插入每一个鼠标函数中:

function mouseDown(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
}
function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
}
function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
}
对此,鼠标的初始化都完成了,而且对于IE和Netscape之间的不同之处也做了特别的处理,于是可以分别对三个鼠标事件进行介绍。

“mouseDown()”事件

现在要让鼠标做一个简单的动作来说明问题:当某个人单击鼠标时,把一个层移动到鼠标所在的位置。首先,要初始化层:

if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
}
然后,使用<DIV>标记建立一个层。于是在“mouseDown()”函数里,使用x和y变量来把刚才建立的层移动到那个值的位置:

function mouseDown(e) {
if(ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragObj.xpos=x
dragObj.ypos=y
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
}
}
或者,为了增加趣味性,在移动的同时把鼠标的位置显示在浏览器的“状态栏”里:

function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
status="x:"+x+"y:"+y
}
完整的“mouseDown()”事件例子如下,当用户在页面任意一点上单击后,事先建立的层就会移动到鼠标所在的位置。

<HTML>

<HEAD>
<TITLE>DHTML Demo</TITLE>
<script language="javascript">
<!--

ns4=(document.layers)?true:false
ie4=(document.all)?true:false

function init() {
if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
}

document.onmousedown=mouseDown
document.onmousemove=mouseMove
document.onmouseup=mouseUp
if(ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}

function mouseDown(e) {
if((ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragObj.xpos=x
dragObj.ypos=y
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
}
}

function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
status="x:"+x+"y:"+y
}


function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
}

//-->
</script>
<style type="text/css">
<!--
#squareDiv{position:absolute;left:100;top:100;width:50;height:50;clip:rect(0,50,50,0);
background-color:blue;layer-background-color:blue;}
-->
</style>
</HEAD>

<BODY onLoad="init()">
<div id="instructions" style="position:absolute;">
<p>在页面上任意地点单击鼠标,方块就会移动到那一点。</p>
</div><div id="squareDiv"></div>
</BODY>
</HTML>
“mouseMove()”事件

不像“mouseDown()”或“mouseUp()”事件,“mouseMove()”函数在鼠标移动的时候总激活的。但是我们并不总是要在鼠标移动的时候做其他事件。比如,在拖动的时候,我们仅仅要在鼠标单击之后才激活鼠标移动的事件。因此,要得到对“mouseMove”事件更多的控制权,可以使用活动变量“dragActive”:

dragActive=false//初始时不激活

然后在“mouseDown()”函数中,先不让层移动到鼠标所在的位置上,而是把“mouseMoveActive”变量值设置为1(true):

function mouseDown(e) {
if((ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragActive=true
}
}
然后再让“mouseMove”来做所有的事情。现在鼠标每次移动时,层总是跟随着鼠标的位置:
function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
status="x:"+x+"y:"+y
if(dragActive){
dragObj.xpos=x
dragObj.ypos=y
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
return false
}
}
上面的代码中加入了一个附加的命令:“return false”。这是很重要的,因为它避免了一个Netscape会出现的问题。这个问题发生在当把一张图片放在CSS层中,如果不是按照上面的做法,不返回“false”,拖放动作将被中止。这里有一个完整的鼠标移动事件例子:

<HTML>

<HEAD>
<TITLE>DHTML Demo</TITLE>
<script language="javascript">
<!--

ns4=(document.layers)?true:false
ie4=(document.all)?true:false

function init() {
if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
}

dragActive=false

document.onmousedown=mouseDown
document.onmousemove=mouseMove
document.onmouseup=mouseUp
if(ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}

function mouseDown(e) {
if((ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragActive=true
}
}

function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
if(dragActive) {
dragObj.xpos=x
dragObj.ypos=y
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
return false
}
}

function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
}
//-->
</script>
<style type="text/css">
<!--
#squareDiv{position:absolute;left:100;top:100;width:50;height:50;clip:rect(0,50,50,0);
background-color:blue;layer-background-color:blue;}
-->
</style>

</HEAD>

<BODY onLoad="init()">
<div id="instructions" style="position:absolute;">
<p>当你在页面上单击后,方块就会跟随鼠标一起移动。</p></div>
<div id="squareDiv"></div>
</BODY>
</HTML>
只要实践一下就一定会发现这个例子其实有一个致命的缺陷。解决这个缺陷的方法我们将在下面的内容中讨论。

“mouseUp()”事件

在上一个例子中,一旦单击了那个对象,便无法把它放下来,要改变这种情况,可以使用我们所要介绍的最后一项鼠标事件“mouseUp”来把“mouseMoveActive”变量设置为0,使“mouseMove”无效:

function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragActive=false
}
为此,我们可以使鼠标在释放时,对象就脱离鼠标的控制,完整的程序如下:
<HTML>

<HEAD>
<TITLE>DHTML Demo</TITLE>
<script language="javascript">
<!--

ns4=(document.layers)?true:false
ie4=(document.all)?true:false

function init() {
if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
}

dragActive=false

document.onmousedown=mouseDown
document.onmousemove=mouseMove
document.onmouseup=mouseUp
if(ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}

function mouseDown(e) {
if((ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragClickX=x-dragObj.xpos
dragClickY=y-dragObj.ypos
dragActive=true
}
}

function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
if(dragActive) {
dragObj.xpos=x-dragClickX
dragObj.ypos=y-dragClickY
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
return false
}
}

function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x;var y=event.y+document.body.scrollTop}
dragActive=false
}

//-->
</script>
<style type="text/css">
<!--
#squareDiv{position:absolute;left:100;top:100;width:50;height:50;clip:rect(0,50,50,0);
background-color:blue;layer-background-color:blue;}
-->
</style>
</HEAD>

<BODY onLoad="init()">
<div id="instructions" style="position:absolute;">
无论你单击页面的何处,方块都会感应到。
<br>当你按下鼠标左键时可以任意拖动方块。
<br>当你释放鼠标后,方块同时也会停止移动。
</div>
<div id="squareDiv"></div>
</BODY>
</HTML>
完美的鼠标拖放

我们已经非常接近成功了!可以看出,上面例子的唯一缺憾就是无论单击页面的什么位置,方块都会跟着移动,如果页面只有一个层,那么问题还不是很大,但是如果有多个层,你一定会被搞糊涂,于是我们想到,应该让方块只有鼠标点击到它上面的情况下,才开始跟随鼠标移动。这种效果已经和Windows中的拖放几乎一样了。

我们可以使用另外一个“mouseDown”函数来检查是否单击在层的上面。

其中最普通的方法就是比较鼠标和层的位置,需要获取层边缘的数值。

在Netscape里,宽度和高度是基于剪切宽度和剪切高度的值:

document.layername.clip.width

document.layername.clip.height

在Internet Explorer里,必须使用“pixelWidth”和“pixelHeight”:

layernames.style.pixelWidth

layernames.style.pixelHeight

因此程序的初始化代码可以替换为包含这些值的“w”和“h”属性:

if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
dragObj.w=dragObj.clip.width
dragObj.h=dragObj.clip.width
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
dragObj.w=dragObj.pixelWidth
dragObj.h=dragObj.pixelHeight
}
于是,使用每一个属性令我们能够设置一个在范围的“if”语句来检验用户是否在层的边框范围内单击过:

if(x>=dragObj.xpos && x<=dragObj.xpos+dragObj.w && y>=dragObj.ypos && y<=dragObj.ypos+dragObj.h)

这一行被插入“mouseDown()”函数来确定用户仅仅在层被单击的情况下才激活拖动事件。

注意:必须在一旦开始拖动后马上在“onMouseDown”中放入一个返回“false”值,这可以避免在Mac机器上产生问题。

我们终于成功地完成了鼠标拖动事件的设计,现在完整地演示一遍吧:

<HTML>

<HEAD>
<TITLE>DHTML Demo</TITLE>
<script language="javascript">
<!--

ns4=(document.layers)?true:false
ie4=(document.all)?true:false

function init() {
if(ns4) {
dragObj=document.squareDiv
dragObj.xpos=dragObj.left
dragObj.ypos=dragObj.top
dragObj.w=dragObj.clip.width
dragObj.h=dragObj.clip.width
}
if(ie4) {
dragObj=squareDiv.style
dragObj.xpos=dragObj.pixelLeft
dragObj.ypos=dragObj.pixelTop
dragObj.w=dragObj.pixelWidth
dragObj.h=dragObj.pixelHeight
}

dragActive=false

document.onmousedown=mouseDown
document.onmousemove=mouseMove
document.onmouseup=mouseUp
if(ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}

function mouseDown(e) {
if((ns4 && e.which==1) || ie4) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x+document.body.scrollLeft;var y=event.y+document.body.scrollTop}
if(x>=dragObj.xpos && x<=dragObj.xpos+dragObj.w && y>=dragObj.ypos && y<=dragObj.ypos+dragObj.h) {
dragClickX=x-dragObj.xpos
dragClickY=y-dragObj.ypos
dragActive=true
return false
}
}
}

function mouseMove(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x+document.body.scrollLeft;var y=event.y+document.body.scrollTop}
if(dragActive) {
dragObj.xpos=x-dragClickX
dragObj.ypos=y-dragClickY
dragObj.left=dragObj.xpos
dragObj.top=dragObj.ypos
return false
}
}

function mouseUp(e) {
if(ns4) {var x=e.pageX;var y=e.pageY}
if(ie4) {var x=event.x+document.body.scrollLeft;var y=event.y+document.body.scrollTop}
dragActive=false
}

//-->
</script>
<style type="text/css">
<!--
#squareDiv{position:absolute;left:100;top:100;width:50;height:50;clip:rect(0,50,50,0);
background-color:blue;layer-background-color:blue;}
-->
</style>
</HEAD>

<BODY onLoad="init()">
<div id="instructions" style="position:absolute;">
<p>无论你单击页面的何处,方块都会感应到。
<br>当你按下鼠标键不放时可以任意拖动方块。
<br>当你释放鼠标后,就把方块放置在当前的地方。
</p>
</div>
<div id="squareDiv"></div>
</BODY>
</HTML>