说到mouse事件,可能会涉及到很多东西。这里主要谈的是鼠标移入,移出事件。我们知道在js中分别是用onmouseover和onmouseout来表示鼠标移入和移出事件,当然也可以用onmouseenter和onmouseleave来表示鼠标移入和移出。但是这两种移入,移出却有很大的区别。

不得不说,有时候某度出来的问题真的会误导人,各种不负责任的转载,所以还是借鉴居多。下面以函数的形式通俗的来说下着onmouseover,onmouseout ,onmouseenter,onmouseleave的概念:

onmouseover — onmouseout

onmouseover

obj.onmouseover=function(){
  fn1();
}

表示当鼠标移入obj时,触发fn1(),但是当鼠标由元素移到子元素上或者由子元素移动到元素时,都会触发fn1(),这个事件可兼容所有浏览器。

onmouseout

obj.onmouseout=function(){
  fn2();
}

表示当鼠标移出obj时,触发fn2(),但是当鼠标由元素移到子元素上或者由子元素移动到元素时,也都会触发fn2(),与onmouseover一样 ,这个事件也兼容所有浏览器。

onmouseenter — onmouseleave

onmouseenter

obj.onmouseenter=function(){
  fn1();
}

表示当鼠标移入obj时,触发fn1(),并且这个事件不冒泡 ,也就是说鼠标在元素的子元素里移动(包含在子元素和元素之间交叉移动)都不会触发fn1(),在DOM2级并没有定义这个事件,在DOM3级才有定义。IE,FF,Opear都支持这个事件,但是chrome并不支持这个事件。

onmouseleave

obj.onmouseleave=function(){
  fn2();
}

表示当鼠标移出obj时,触发fn2(),并且这个事件不冒泡 ,也就是说鼠标在元素的子元素里移动(包含在子元素和元素之间交叉移动)都不会触发fn2(),在DOM2级并没有定义这个事件,而在DOM3级才有定义。IE,FF,Opear都支持这个事件,但是chrome并不支持这个事件。

实例

这么一个需求,有一个id为box,背景为灰色的div,其有一个id为con,且背景色为白色的子div。如代码,我们需要的是,当鼠标移入box时,隔0.1s后box的背景色变为绿色。鼠标移出box时,box的背景色恢复为灰色。

<style>
#box{width:100px;height:90px;background:#ccc;padding:30px 30px 50px;}
#con{height:80px;background:#fff;}
</style>

<body id="body">
  <div id="box">
    box
    <div id="con">con</div>
  </div>
</body>

<script>
(function(){
  function $(id){
    return document.getElementById(id);
  }
  var oBox=$("box");
  oBox.onmouseover=function(){  // 这里不用onmouseenter,因为chrome不兼容
    timer=setTimeout(function(){oBox.style.background="green";},100);
    document.title="移入";
  }
  oBox.onmouseout=function(){
    oBox.style.background="#ccc";
    document.title="移出";
  }
})()
</script>

观察上图,我们发现当鼠标在box内移动时,box的背景会不断闪烁变化。这就是onmouseover本来的特性所导致的,在元素与子元素间移动会重复触发fn1()和fn2()。但是文件的title却很正常,如果鼠标在box内,title显示的一直是“移入”。而鼠标在box外,则title显示的一直是“移出”。

jquery方法

在jquery中,这个需求可以用hover函数轻易实现,如:

obj.hover(fn1(),fn2()) ;  //具体代码省略

由此可知,jquery里的hover=mouseenter+mouseleave,而非hover=mouseover+mouseout,由于jquery良好的封装性,因此mouseenter在chrome中也可以得到很好的支持,但是如何让原生的onmouseenter也支持chrome呢?

由于onmouseover兼容性不错,所以我们想到用onmouseover来模拟onmouseenter。那么如何模拟呢?这里我们可以通过此两者的区别考虑,也就是说无论鼠标在哪里,只要鼠标在box元素里移动(无论是从box元素到con元素,还是从con元素到box元素),都不触发fn1()。同理,只有当鼠标真正离开了box元素(不是从con元素离开到box元素,或者是从box元素离开到con元素),才触发fn2();

可是,又如何知道鼠标是从哪个元素移入到另外一个元素呢?这里,javascript给我们提供了一个relatedTarget 事件属性:

对于onmouseover 事件来说,该属性是鼠标指针移到目标节点上时所离开的那个节点。(IE是fromElement)

对于onmouseout 事件来说,该属性是离开目标时,鼠标指针进入的节点。(IE是toElement)

比如说:鼠标由box元素移入到它的子元素con,则此时,对onmouseover来说,box就是relatedTarget节点。而对onmouseout来说,con就是relatedTarget节点。于是我们对这两个节点判断,在onmouseover时,如果两个节点存在父子关系,则不触发fn1()。而在box内移动时,当发生onmouseout事件,如果两个节点存在父子关系,则不触发fn2();

判断父子关系的函数:

function isSons(obj1,obj2){
  var temp=obj1;
  while(temp.parentNode){
    temp=temp.parentNode;
    if(temp==obj2){
      return true;
    }
  }
  return false;
}

为了方便起见,我们利用几个标签(id分别为relEle1,relEle2的span元素)来表示鼠标移入,移出元素时relatedTarget的变化。于是就有:

<body id="body">
  <div id="box">
    box
    <div id="con">con</div>
  </div>
</body>

<p>isSons(this,relEle):<span id="status"></span></p>
<p>this:<span id="This"></span></p>
<p>relEle:<span id="relEle1"></span></p>
<p>relEle2:<span id="relEle2"></span></p>

<script>
(function(){
  function $(id){
    return document.getElementById(id);
  }

  function isSons(obj1,obj2){
    var temp=obj1;
    while(temp.parentNode){
      temp=temp.parentNode;
      if(temp==obj2){
        return true;
      }
    }
    return false;
  }

  var oBox=$("box"),oS=$("status"),oT=$("This"),oR1=$("relEle1"),oR2=$("relEle2");

  oBox.onmouseover=function(ev){
    var e=ev||event,relEle= e.relatedTarget||e.fromElement;
    if(isSons(this,relEle)){ //为父子关系才触发,但是这里this为box元素,当在box内移动时,relEle不是box本身就是con,所以永远不会触发
      setTimeout(function(){oBox.style.background="green";},100);
    }
    oS.innerHTML=isSons(this,relEle);
    oT.innerHTML=this.id;
    oR1.innerHTML=relEle.id;
    document.title="移入";
  }
  oBox.onmouseout=function(ev){
    var e=ev||event,relEle= e.relatedTarget||e.toElement;
    if(isSons(this,relEle)){
      oBox.style.background="#ccc";
    }
    oR2.innerHTML=relEle.id;
    document.title="移出";
    console.log(isSons(this,relEle));
  }
})()
</script>

通过代码我们可知,当鼠标在box内部移动时,isSons(this,relEle)总是false(我们可以看到控制台的变化),则永远不会触发fn1()和fn2()。只有当鼠标由外部移入box时,isSons(this,relEle)才为true,此时才会触发fn1(),或者当鼠标由box移出到外部时,才会触发fn2()。效果演示图如下: