Javascript拖拽&拖放系列文章3之细说事件对象

  在阅读本文前,可以先阅读《Javascript拖拽&拖放系列文章2之offsetLeft、offsetTop、offsetWidth、offsetHeight属性》这篇文章,以理清上下文关系。

  好了,让我们开始进入正题。

  模型相同的属性/方法

  1 Button属性

  Integer类型,可读可写。对于特定的鼠标事件,表示按下的鼠标按钮,它可以在拖拽的时候,判断是否是鼠标左键引发mousedown事件。它的所有取值及其意义(参考自《Javascript高级程序设计》)好了,让我们开始进入正题。

  3.1 e/window.Event对象的属性/方法

  3.1.1 IE事件模型和DOM事件如下:

  0-未按下按钮

  1-按下左键

  2-按下右键

  3-同时按下左右按钮

  4-按下中键

  5-按下左键和中键

  6-按下右键和中键

  7同时按下左中右键

  mouseup的button属性返回的数值和mousedown事件中的完全一样。

  注:在兼容DOM事件模型的所有浏览器中,0表示按下左键,数值1并不存在,2表示按下右键,对于非鼠标事件,返回“undefined”。

  2 clientX、clientY属性

  这两个属性的类型都是Integer,单位是像素,可读可写。分别表示相关事件发生时,鼠标在浏览器的客户端区域(不包括工具栏、滚动条等)的x坐标和y坐标。用一张图片来解释就再好不过了,请看:

Javascript拖拽&拖放系列文章3之细说事件对象

  

图3.1:clientX和clientY属性

以下代码是一个非常简单的示例,它可以随时定位鼠标的坐标,并将结果显示在两个文本框中,如果愿意的话,你可以尝试运行它,兼容目前所有的现代浏览器。

示例代码1:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

  <html>

  <head>

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <title>Untitled Document</title>

  <style type="text/css">

  body{

  border:1px solid black;

  margin:0px;

  }

  </style>

  <script type="text/javascript" language="JavaScript">

  document.onmousemove=move;

  function move(e){

  if(!e)

  {

  e=window.event;

  }

  var xElement=document.getElementById("x");

  var yElement=document.getElementById("y");

  xElement.value=e.clientX;

  yElement.value=e.clientY;

  }

  </script>

  </head>

  <body>

  <input type="text" id="x" value="" />

  <input type="text" id="y" value="" />

  </body>

  </html>

  3 type属性

  String类型,在IE中可读可写,而在兼容DOM事件模型的浏览器中为只读属性。

例如,你可以使用如下代码在任何浏览器中获取事件的类型:

 var e=e||window.event;

  var sEventType=e.type;

如果是IE浏览器,e的类型就是“undefined”,从而将window.Event对象赋给变量e,对于其他的浏览器,结果正好相反。

既然说到了事件的类型,那我们就讨论一下mouseout和mouseover这两个事件了吧,从而为后面将会讲到的fromElement和toElement属性作铺垫。

mouseout事件表示,当鼠标指针在目标元素内,且当指针正要被移出目标元素的边界(也就是CSS中的border属性)时发生。

好了,该是看看我们本文的第二个例子的时候了,紫色边框就是目标元素的边界,你可以将鼠标指针慢慢移向紫色边框,当靠近它时,便会弹出一个对话框。

示例代码2:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

  <html>

  <head>

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <title>Untitled Document</title>

  <style type="text/css">

  body{

  border:1px solid black;

  margin:0px;

  }

  #mouseoutElement{

  width:188px;

  height:188px;

  border:1px solid purple;

  margin-left:188px;

  margin-top:188px;

  }

  </style>

  <script type="text/javascript" language="JavaScript">

  function moveout(e){

  alert("The mouse pointer are moving out from me!!");

  }

  </script>

  </head>

  <body>

  <div id="mouseoutElement">Please move out me!!</div>

  <script type="text/javascript" language="JavaScript">

  var source=document.getElementById("mouseoutElement");

  source.onmouseout=moveout;

  </script>

  </body>

  </html>

Javascript拖拽&拖放系列文章3之细说事件对象

图二:当将鼠标指针移向边界时,弹出了一个对话框

如果你问为什么包含getElementById的代码块要放在Body元素的末尾,那是因为只有这时候Body元素以及其子元素才初始化完成,getElementById才能够取到值,否则就会发生错误。当然,你也可以将代码块封装到Body元素的load事件处理函数当中,效果其实是一样的。

但是mouseout事件在WebKit内核浏览器(Safari、Chrome)的实现当中存在Bug,如果事件处理程序中包括alert函数,就会连续运行两次,所以它会连续弹出两个相同的对话框。这个Bug我也不知道该如何解决,如果有高手知道的话,麻烦你请告诉我。

用下面的代码在Safari(Chrome)中测试我们可以看到,如果在mouseout  Handler中没有alert函数,一切就都正常了。但是mouseout事件在WebKit内核浏览器(Safari、Chrome)的实现当中存在Bug,因为它会连续弹出两个相同的对话框。这个Bug我也不知道该如何解决,如果有高手知道的话,麻烦你请告诉我。

注:感谢alertabc提供了下面的测试代码

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

  <html>

  <head>

  <title>onmouseout</title>

  </head>

  <body>

  <div onmouseout="out();" style=" background-color:Yellow; width:400px; height:250px;"></div>

  <div id="result"></div>

  </body>

  <script type="text/javascript">

  function out(){

  document.getElementById("result").innerHTML += "onmouseout ";

  //alert("h");

  }

  </script>

  </html>

Any help would be greatly appreciated,thanks in anticipation.

I often think that,if all web explorers were to have the same kernel engines,that would be wonderful!

mouseover事件正好和mouseout事件相反,是指当鼠标指针移出某个元素,移动到目标元素的边界(也就是它的border)时发生。

好了再让我们看一个示例代码吧,这个例子和上面的那个类此,因此不再赘述,你可以自己试一下。

示例代码3:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

  <html>

  <head>

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <title>Untitled Document</title>

  <style type="text/css">

  body{

  border:1px solid black;

  margin:0px;

  }

  #mouseoverElement{

  width:188px;

  height:188px;

  border:1px solid purple;

  margin-left:188px;

  margin-top:188px;

  }

  </style>

  <script type="text/javascript" language="JavaScript">

  function moveover(e){

  alert("The mouse pointer are moving into me!!");

  }

  </script>

  </head>

  <body>

  <div id="mouseoverElement">Please move into me!!</div>

  <script type="text/javascript" language="JavaScript">

  var source=document.getElementById("mouseoverElement");

  source.onmouseover=moveover;

  </script>

  </body>

  </html>

WebKit内核浏览器对于mouseout的Bug在mouseover中并不存在。

3.1.2 IE事件模型和DOM事件模型不同的属性/方法

接下来,讨论一下它们之间的区别(只是名字不一样罢了)。

1 IE中的srcElement属性和DOM中的target属性

srcElement和target属性的返回类型都是Element,表示触发事件的元素。例如,

document.body.onmouseover=function(){

  if(arguments[0])

  {

  alert(arguments[0].target.tagName);

  }

  else

  {

  alert(window.event.srcElement.tagName);

  }

  };

这样就可以获得触发了事件的元素了。arguments集合表示方法中的实参集合,在上面的代码段中,arguments[0]如果不为空,就证明是DOM事件模型的浏览器(因为会传递e事件对象),反之,则为IE浏览器。这不难理解。

2 IE中的fromElement、toElement属性和DOM中的relatedTarget属性

这三个属性的返回类型都是Element,在mouseover和mouseout两个鼠标事件中使用。DOM中的relatedTarget相当于IE中的fromElement和toElement属性。当在mouseover事件处理程序当中,relatedTargent属性返回一个元素的引用,表示鼠标指针来自的那个元素。而在mouseout中,relatedTarge属性指出鼠标指针将要去往的,并且是紧靠着当前元素的元素。

注:在mouseout事件中,DOM的target总是等于relatedTarget属性,表示将要去往的元素,如果想要得到触发mouseout事件的当前元素,请使用DOM的currentTarget属性。

在IE中,DOM的relatedTarget被分成了两个属性。对于mouseover事件,fromElement属性表示鼠标指针所离开的,并且是紧靠着当前元素的元素。toElement总是等于srcElement属性。而对于mouseout事件,toElement属性表示鼠标指针将要去往的,并且是紧靠着当前元素的元素。fromElement总是等于srcElement属性。如果有些乱的话,我们不妨看一个例子,你运行一下,就都明白了。

示例代码4:

Javascript拖拽&拖放系列文章3之细说事件对象
Javascript拖拽&拖放系列文章3之细说事件对象
HTML代码
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<style type="text/css">
body{
border:1px solid black;
margin:0px;
height:288px;
}
#mouseoverElement{
width:188px;
height:188px;
border:1px solid purple;
margin:38px 0 0 188px;
}
</style>
<script type="text/javascript" language="JavaScript">
function moveover(e){
alert("The mouse pointer are moving into me!!");
}
</script>
</head>
<body>
<div id="mouseoverElement">Please move into me!!</div>
<script type="text/javascript" language="JavaScript">
var source=document.getElementById("mouseoverElement");
source.onmouseover=function(){
if(arguments[0])
{
alert("The event of mouseover started.");
alert("The element of trigering event is "+arguments[0].target.tagName);
alert("relatedTarget:"+arguments[0].relatedTarget.tagName);
alert("The event of mouseover finished.");
}
else
{
alert("The event of mouseover started.");
alert("The element of trigering event is "+window.event.srcElement.tagName);
alert("fromElement: "+window.event.fromElement.tagName);
alert("toElement: "+window.event.toElement.tagName);
alert("The event of mouseover finished.");
}
};
source.onmouseout=function(){
if(arguments[0])
{
alert("The event of mouseout started.");
alert("The element of trigering event is "+arguments[0].currentTarget.tagName);
alert("relatedTarget:"+arguments[0].relatedTarget.tagName);
alert("The event of mouseout finished.");
}
else
{
alert("The event of mouseout started.");
alert("The element of trigering event is "+window.event.srcElement.tagName);
alert("toElement: "+window.event.toElement.tagName);
alert("fromElement: "+window.event.fromElement.tagName);
alert("The event of mouseover finished.");
}
};
");
}
};
</script>
</body>
</html>

  3.2 将IE事件模型和DOM事件模型的差异封装起来

注:本来打算采用《Javascript高级程序设计》这本书中所叙述的方法来封装差异,读过的人知道,作者是用return EventUtil.getEvent.caller.Arguments[0]来获得原始的事件对象的,这的确很棒,很巧妙,已经很完美了,我没有必要在re-invent the wheel了,只可惜Opera浏览器不支持caller属性,为了浏览器兼容性,所以我不得不采用自己的方法了。caller的详细内容可以参考全面理解JavaScript中的caller,callee,call,apply 这篇文章

3.2.1 定义框架

首先定义一个封装的框架,再细细添枝加叶。

var EventUtilization=new Object;

  EventUtilization.getCompatibleEvent=function(e){

  //判断是否是IE浏览器

   if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //使IE的window.event属性和DOM的一样

   //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   return e;

  }

  return e;

  }

很简单,就不再赘述了。

3.2.2 格式化window.Event对象

1 构造DOM中的pageX和pageY属性

这两个属性分别表示鼠标指针相对于整张网页(取决于body元素的实际边界范围)的x、y坐标,单位是像素。在构造它们之前,需要了解另外两个属性document.Body.scrollLeft和document.body.scorllTop,单位是像素,当浏览器出现滚动条且滚动页面时,数值分别等同于页面在水平、垂直方向上滚动的距离,否则为0像素。

我想你已经想到了该如何构造pageX和pageY了。没错,pageX=clientX+scorllLeft,pageY=clientY+clientTop。

Javascript代码片段:

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  e.pageX=e.clientX+document.body.scrollLeft;

  e.pageY=e.clientY+document.body.scrollTop;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

2 构造DOM中的relatedTarget属性

我们可以用事件对象的type属性判断鼠标事件类型,从而决定何时将fromElement或toElement的值赋给relatedTarget属性。

Javascript代码片段:

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   if(e.type=="mouseout")

  {

  e.relatedTarget=e.toElement;

  }

  else if(e.type=="mouseover")

  {

  e.relatedTarget=e.fromElement;

  }

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

3 构造DOM中的target/currentTarget属性

终于快要结尾了......

只需要引用srcElement属性就可以了,不过要注意,我们在前面说过,在mouseout事件中,DOM的target总是等于relatedTarget属性,因此我们同样要构造currentTarget属性,以免在调用currentTarget时返回“undefined”。

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   e.target=e.srcElement;

  e.currentTarget=e.srcElement;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

完整的JS代码:

Javascript拖拽&拖放系列文章3之细说事件对象
Javascript拖拽&拖放系列文章3之细说事件对象
 
var EventUtilization=new Object;
EventUtilization.getCompatibleEvent=function(e){
if(!EventUtilization.getCompatibleEvent.arguments[0])
{
e=window.event;
e.pageX=e.clientX+document.body.scrollLeft;
e.pageY=e.clientY+document.body.scrollTop;
if(e.type=="mouseout")
{
e.relatedTarget=e.toElement;
}
else if(e.type=="mouseover")
{
e.relatedTarget=e.fromElement;
}
e.target=e.srcElement;
e.currentTarget=e.srcElement;
return e;
}
return e;
}

它能够完美的兼容Internet Explorer、Mozilla Firefox、Netscape、Sarari(Chrome)、Opera等浏览器的流行版本。

3.2.3 框架的使用

我将它命名为EventUtilization.js,将它放在页面中后,就可以用如下的方法使用了:

  var source=document.getElementById("mouseoverElement");

  source.onmouseover=function(e){

  var e=EventUtilization.getCompatibleEvent(e);

  alert(e.pageX);

  alert(e.pageY);

  alert(e.relatedTarget.tagName);

  alert(e.target.tagName);

  alert(e.currentTarget.tagName);

  };

啊,冗长的一篇文章被我写完了,我终于可以休息一下了........希望能给一些人帮助,哪怕是一点点,也会是我继续写下去的动力........

至于下一篇写什么?卖个关子先。

To be continued........

yle="COLOR: #0000ff"></script>

  </body>

  </html>


  3.2 将IE事件模型和DOM事件模型的差异封装起来

注:本来打算采用《Javascript高级程序设计》这本书中所叙述的方法来封装差异,读过的人知道,作者是用return EventUtil.getEvent.caller.Arguments[0]来获得原始的事件对象的,这的确很棒,很巧妙,已经很完美了,我没有必要在re-invent the wheel了,只可惜Opera浏览器不支持caller属性,为了浏览器兼容性,所以我不得不采用自己的方法了。caller的详细内容可以参考全面理解JavaScript中的caller,callee,call,apply 这篇文章

3.2.1 定义框架

首先定义一个封装的框架,再细细添枝加叶。

var EventUtilization=new Object;

  EventUtilization.getCompatibleEvent=function(e){

  //判断是否是IE浏览器

   if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //使IE的window.event属性和DOM的一样

   //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   return e;

  }

  return e;

  }

很简单,就不再赘述了。

3.2.2 格式化window.Event对象

1 构造DOM中的pageX和pageY属性

这两个属性分别表示鼠标指针相对于整张网页(取决于body元素的实际边界范围)的x、y坐标,单位是像素。在构造它们之前,需要了解另外两个属性document.Body.scrollLeft和document.body.scorllTop,单位是像素,当浏览器出现滚动条且滚动页面时,数值分别等同于页面在水平、垂直方向上滚动的距离,否则为0像素。

我想你已经想到了该如何构造pageX和pageY了。没错,pageX=clientX+scorllLeft,pageY=clientY+clientTop。

Javascript代码片段:

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  e.pageX=e.clientX+document.body.scrollLeft;

  e.pageY=e.clientY+document.body.scrollTop;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

2 构造DOM中的relatedTarget属性

我们可以用事件对象的type属性判断鼠标事件类型,从而决定何时将fromElement或toElement的值赋给relatedTarget属性。

Javascript代码片段:

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   if(e.type=="mouseout")

  {

  e.relatedTarget=e.toElement;

  }

  else if(e.type=="mouseover")

  {

  e.relatedTarget=e.fromElement;

  }

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

3 构造DOM中的target/currentTarget属性

终于快要结尾了......

只需要引用srcElement属性就可以了,不过要注意,我们在前面说过,在mouseout事件中,DOM的target总是等于relatedTarget属性,因此我们同样要构造currentTarget属性,以免在调用currentTarget时返回“undefined”。

    if(!EventUtilization.getCompatibleEvent.arguments[0])

  {

  e=window.event;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   e.target=e.srcElement;

  e.currentTarget=e.srcElement;

  //

Javascript拖拽&拖放系列文章3之细说事件对象

Javascript拖拽&拖放系列文章3之细说事件对象

  ..

   }

完整的JS代码:

Javascript拖拽&拖放系列文章3之细说事件对象
Javascript拖拽&拖放系列文章3之细说事件对象
 
var EventUtilization=new Object;
EventUtilization.getCompatibleEvent=function(e){
if(!EventUtilization.getCompatibleEvent.arguments[0])
{
e=window.event;
e.pageX=e.clientX+document.body.scrollLeft;
e.pageY=e.clientY+document.body.scrollTop;
if(e.type=="mouseout")
{
e.relatedTarget=e.toElement;
}
else if(e.type=="mouseover")
{
e.relatedTarget=e.fromElement;
}
e.target=e.srcElement;
e.currentTarget=e.srcElement;
return e;
}
return e;
}

它能够完美的兼容Internet Explorer、Mozilla Firefox、Netscape、Sarari(Chrome)、Opera等浏览器的流行版本。

3.2.3 框架的使用

我将它命名为EventUtilization.js,将它放在页面中后,就可以用如下的方法使用了:

  var source=document.getElementById("mouseoverElement");

  source.onmouseover=function(e){

  var e=EventUtilization.getCompatibleEvent(e);

  alert(e.pageX);

  alert(e.pageY);

  alert(e.relatedTarget.tagName);

  alert(e.target.tagName);

  alert(e.currentTarget.tagName);

  };

啊,冗长的一篇文章被我写完了,我终于可以休息一下了........希望能给一些人帮助,哪怕是一点点,也会是我继续写下去的动力........

至于下一篇写什么?卖个关子先。

To be continued........