微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JS中mouseover和mouseout多次触发问题如何解决

问题描述

我希望当鼠标移动到id1上的时候,id2显示,当鼠标离开id1的时候,id2显示。问题如下:

1.当鼠标从id1上移动到id2上的时候,id由有显示变为不显示,然后变为显示

2.当鼠标从id2上移动到id1上的时候, id2有显示变为不显示,然后变为显示

我希望的是当鼠标在id1或者id2上移动的时候,id2一直显示,不发生变化。

rush:js;">

这里写图片描述

问题解决办法

最开始的问题分析,当鼠标从id1上移动到id2上的时候,由于鼠标由id2离开进入id1,针对id1触发了一个mouSEOut事件,于是id2有显示变为不显示,接着在鼠标移动到id2上,在id2上触发了一个mouSEOver事件,由于冒泡机制,id2上的mouSEOver冒泡到id1之前,触发了id1上的mouSEOver事件,然后id2由不显示变为显示。同理,当鼠标从id2上移动到id1上的时候,针对id2,触发了一个mouSEOut事件,还是因为冒泡机制,mouSEOut事件传到id1上,id2由显示变为不显示,接着鼠标移动到id1之前,触发了一个mouSEOver事件,然后id2有不显示变为显示

看来,上面的问题归根要解决的是,当鼠标由id1上移动到id2上的时候,阻止id1的mouSEOut事件;当鼠标从id2上移动到id1上的时候,阻止id2的mouSEOut事件冒泡到id1之上。那么仅仅通过阻止冒泡是解决不了问题。

为了解决这样的问题,jQuery提供了mouseenter和mouseleave方法。于是将JS代码改为如下,很好解决了问题。

rush:js;"> $("#id1").mouseenter(function(){ $(this).children().fadeIn(1000); }).mouseleave(function(){ $(this).children().fadeOut(1000); });

很多地方都有介绍mouseenter、mouseleave与mouSEOver、mouSEOut,于是复制粘贴了一个

/*********************************************************/

1.mouSEOver与mouseenter

不论鼠标指针穿过被选元素或其子元素,都会触发 mouSEOver 事件。

只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。

2.mouSEOut与mouseleave

不论鼠标指针离开被选元素还是任何子元素,都会触发 mouSEOut 事件。 只有在鼠标指针离开被选元素时,才会触发 mouseleave 事件。

/*********************************************************/

现象确实是这个现象,但是过程说的有点模糊,我的理解如下:

当鼠标指针移动到被选元素,会触发 mouSEOver 事件,这个大家都知道,当鼠标指针由被选元素移动到其子元素,先是触发被选元素的mouSEOut事件,然后子元素的mouSEOver事件冒泡到被选元素,此时相当于被选元素先执行了一个mouSEOut事件,然后执行一个mouSEOver事件。

为了验证将代码改为如下

rush:js;">

鼠标从页面移动到id1,然后由id1移动到id2上,控制台输出如下图

这里写图片描述

可以看出id1先后调用了mouSEOver、mouSEOut、mouSEOver事件,正好和上面分析的相同。

mouseenter与mouseleave实现分析

原理分析

从上面分析,我们可以看出,要实现mouseenter与mouseleave的效果,就是当鼠标从被选元素移动到其子元素上的时候,被选元素不执行mouSEOut事件,也不执行子类冒泡过来的mouSEOver事件,当鼠标从被选元素子元素移动到被选元素上的时候,被选元素不执行mouSEOver事件,也不执行子类冒泡过来的mouSEOut事件。

要实现上面的效果,我们需要event对象的一个属性relatedTarget,这个属性就是用来判断 mouSEOver和mouSEOut事件目标节点的相关节点的属性。简单的来说就是当触发mouSEOver事件时,relatedTarget属性代表的就是鼠标刚刚离开的那个节点,当触发mouSEOut事件时它代表的是鼠标移向的那个对象。由于MSIE不支持这个属性,不过它有代替的属性,分别是 fromElementtoElement。除此,我们还需要contains方法,来判断一个对象是否包含在另外一个对象中。

这样当鼠标移动,需要判断以下两条即可

1.调用mouSEOver,只需要判断relatedTarget是否被选元素的子元素,如果是,则不执行(当于从被选元素子元素移动到被选元素,不执行mouSEOver;当于从被选元素移动到被选元素子元素,不执行冒泡过来的mouSEOver);

2.调用mouSEOut,只需要判断relatedTarget是否被选元素的子元素,如果是,则不执行(当于从被选元素子元素移动到被选元素,不执行子元素冒泡过来的mouSEOut;当于从被选元素移动到被选元素子元素,不执行mouSEOver);

实现过程

判断两个元素是否存在包含关系

jquery中封装了contains函数如下

这里写图片描述

可以简化为如下

rush:js;"> //判断两个a中是否包含b function contains(a,b){ return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); }

compareDocumentPosition介绍

这个方法是 DOM Level 3 specification 的一部分,允许你确定 2 个 DOM Node 之间的相互位置。这个方法比 .contains() 强大。这个方法一个可能应用是排序 DOM Node 成一个详细精确的顺序。NodeA.compareDocumentPosition(NodeB)返回的信息描述如下:

比特 序号 意义

通过上面我们就可以理解为什么要写成a.compareDocumentPosition(b) & 16因为如果节点 A 包含节点 B,就会返回16,16&16=1,其他的情况结果都会0。

获取兼容性性的relatedTarget

为了兼容各种浏览器,参考jquery源码,写出如下代码,来获取mouSEOver和mouSEOut事件目标节点的相关节点的属性relatedTarget

rush:js;"> function getRelated(e){ var related; var type=e.type.toLowerCase();//这里获取事件名字 if(type=='mouSEOver'){ related=e.relatedTarget||e.fromElement }else if(type='mouSEOut'){ related=e.relatedTarget||e.toElement } return related; }

改进mouSEOver和mouSEOut

改进mouSEOver和mouSEOut以实现改进mouseenter与mouseleave效果,所有代码如下。

rush:js;">

测试,鼠标移动路线如下图路线

由控制台可以很看出,此刻的mouSEOver和mouSEOut已经完全具备mouseenter与mouseleave效果效果

这里写图片描述

代码的封装

如果每次进行这样的操作,都需要加载Jquery或是写很多代表,将是件繁琐的事,为了便于以后操作,进行了适当的封装,模拟Jquery,生成自己的mouseenter与mouseleave。代码封装到dqMouse.js文件中,如下:

rush:js;"> (function(w){ var dqMouse = function(obj) { // 函数体 return new dqMouse.fn.init(obj); } dqMouse.fn = dqMouse.prototype = { // 扩展原型对象 obj:null,dqMouse: "1.0.0",init: function(obj) { this.obj=obj; return this; },contains:function(a,b) { return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); },getRelated:function(e) { var related; var type=e.type.toLowerCase();//这里获取事件名字 if(type=='mouSEOver'){ related=e.relatedTarget||e.fromElement }else if(type='mouSEOut'){ related=e.relatedTarget||e.toElement } return related; },over:function(fn){ var obj=this.obj; var _self=this; obj.onmouSEOver=function(e){ var related=_self.getRelated(e); if(this!=related && !_self.contains(this,related)){ fn(); } } return _self; },out:function(fn){ var obj=this.obj; var _self=this; obj.onmouSEOut=function(e){ var related=_self.getRelated(e); if(obj!=related && !_self.contains(obj,related)){ fn(); } } return _self; } } dqMouse.fn.init.prototype = dqMouse.fn; window.dqMouse = window.$$= dqMouse; })(window);

调用的源文件如下:

rush:js;">

以上所述是小编给大家介绍的JS中mouSEOver和mouSEOut多次触发问题如何解决的相关内容,希望对大家有所帮助!

原文地址:https://www.jb51.cc/js/48126.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐