JavaScript中mouseover和mouseout多次触发解决办法

问题描述

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

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

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

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

<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div id="id1" style="width:800px; height:400px; background-color:#F23">
        <div id="id2" style="width:400px; height:300px; background-color:#0F8; display:none;">
        </div>
    </div>
<script type="text/javascript">
    $("#id1").mouseover(function(){
                $(this).children().fadeIn(1000);
            }).mouseout(function(){
                $(this).children().fadeOut(1000);
            });
</script>

问题解决办法

最开始的问题分析,当鼠标从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代码改为如下,很好解决了问题。

$("#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事件。

为了验证将代码改为如下

<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div id="id1" style="width:800px; height:400px; background-color:#F23">
        <div id="id2" style="width:400px; height:300px; background-color:#0F8;  position:absolute; top:300px;">
        </div>
    </div>

<script type="text/javascript">
$("#id1").mouseover(function(){
                //$(this).children().fadeIn(1000);
                console.log(‘a‘);
            }).mouseout(function(){
                //$(this).children().fadeOut(1000);
                console.log(‘b‘);
            });
</script>

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

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

mouseenter与mouseleave实现分析

原理分析

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

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

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

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

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

实现过程

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

jquery中封装了contains函数如下

可以简化为如下

//判断两个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)返回的信息描述如下:

比特 序号 意义
000000 0 元素一致
000001 1 节点在不同的文档(或者一个在 文档之外)
000010 2 节点 B 在节点 A 之前
000100 4 节点 A 在节点 B 之前
001000 8 节点 B 包含节点 A
010000 16 节点 A 包含节点 B
100000 32 浏览器的私有使用

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

获取兼容性性的relatedTarget

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

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效果,所有代码如下。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div id="id1" style="width:800px; height:400px; background-color:#F23">
        <div id="id2" style="width:400px; height:300px; background-color:#0F8;  position:absolute; top:300px;">
        </div>
    </div>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script type="text/javascript">
    //判断两个a中是否包含b
    function contains(a,b){
            return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16);
    }
    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;
    }

        $(function(){
            $("#id1").mouseover(function(e){
                //判断鼠标从哪移到id1上面
                var related=getRelated(e);
                //如果related是id1的子元素id2,即从子元素id2移动到id1,或是related为id1,即从id1移动到其子元素id2上面,则不进行任何操作,否则进行相应的操作
                if(this!=related && !contains(this,related)){
                    console.log(‘mouseover‘);
                }

            }).mouseout(function(e){
                //判断鼠标要从id1上面移动到哪去?
                var related=getRelated(e);
                //如果related是id1,即当id1从其子元素移动到id1上,或是related是id2,即从id1上移动到其子元素,不进行任何操作,否则进行相应的操作
                if(this !=related && !contains(this,related)){
                    console.log(‘mouseout‘);
                }
            });
        });

    </script>
</body>
</html>

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

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

代码的封装

如果每次进行这样的操作,都需要加载Jquery或是写很多代码,将是件繁琐的事,为了便于以后操作,进行了适当的封装,模拟Jquery,生成自己的mouseenter与mouseleave。代码封装到dqMouse.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);

调用的源文件如下:

<div id="id1" style="width:800px; height:400px; background-color:#F23">
        <div id="id2" style="width:400px; height:300px; background-color:#0F8;  position:absolute; top:300px;">
        </div>
    </div>
    <script type="text/javascript" src="dqMouse.js"></script>
    <script type="text/javascript">
    var id1=document.getElementById(‘id1‘);

    $$(id1).over(function(){
        console.log(‘mouseover‘);
    }).out(function(){
        console.log(‘mouseout‘);
    });
    </script>

经过测试okey。

********备注******

该博文6月5号已发,但是在写另一篇文章的时候,竟然被覆盖了,感觉还是蛮重要的,于是重新写了。

时间: 2024-10-12 19:14:26

JavaScript中mouseover和mouseout多次触发解决办法的相关文章

js当中mouseover和mouseout多次触发解决办法

问题描述 我希望当鼠标移动到id1上的时候,id2显示,当鼠标离开id1的时候,id2显示.问题如下: 1.当鼠标从id1上移动到id2上的时候,id由有显示变为不显示,然后变为显示 2.当鼠标从id2上移动到id1上的时候, id2有显示变为不显示,然后变为显示 我希望的是当鼠标在id1或者id2上移动的时候,id2一直显示,不发生变化. <script type="text/javascript" src="https://code.jquery.com/jquer

jq:mouseover和mouseout多次触发解决办法

区别: mouseover与mouseenter 不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件. 只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件. mouseout与mouseleave 不论鼠标指针离开被选元素还是任何子元素,都会触发 mouseout 事件. 只有在鼠标指针离开被选元素时,才会触发 mouseleave 事件. 在#a没有子元素的情况下,两者在效果上没有区别 但是#a有子元素的情况下,为了mouseover,mouseout触发的

mouseover和mouseout多次触发解决方法(兼容ie和firefox)(转)

在用到mouseover和mouseout事件来作为事件触发的条件,但是如果我们用做触发的元素内部有其他的元素的时候当鼠标移上的时候会反复的触发mouseover和mouseout事件,如导致菜单闪烁等问题.因为内部元素在鼠标移上的时候会向它的父对象派发事件,所以外面元素相当于也触发了mouseover 事件. 为了阻止mouseover和mouseout的反复触发,这里要用到event对象的一个属性relatedTarget,这个属性就是用来判断 mouseover和mouseout事件目标节

实战中总结出来的CSS常见问题及解决办法

一.ul标签在Mozilla中默认是有padding值的,而在IE中只有margin有值. 二.同一个的class选择符可以在一个文档中重复出现,而id选择符却只能出现一次. 对一个标签同时使用class和id进行CSS定义,如果定义有重复,id选择符做的定义有效,是因为id的权重要比class大. 三.一个兼容性调整(IE和Mozilla)的笨办法: 初学可能会碰到这样一个情况:同样一个标签的属性在IE设置成A显示是正常的,而在Mozilla里必须要设成B才能正常显示,或者两个倒过来. 临时解

eclipse中的js文件报错的解决办法

在使用别人的项目的时候,导入到eclipse中发现js文件报错,解决办法是关闭eclipse的js校验功能. 三个步骤: 1. Eclipse代码   右键点击项目->properties->Validation->Errors/Warming 将Enable Javascript Sematic validation前面的钩子去掉 2.打开.project 文件下面代码去掉 Eclipse代码   <buildCommand> <name>org.eclipse

c++中两个头文件定义同名类的解决办法

今天考虑了一个问题,如果两个头文件比如time.h times.h里面都定义了一个time的类,要怎么解决?vs编译器只对cpp文件进行编译,在编译阶段,这两个头文件的实现文件都不会出错,如果不在主函数中用到time这个类,程序也不会有问题.但是如果用到,那就是disaster!!!,如果你不得不在两个头文件中定义同名类,下面是我自己思考出来的最简单的解决方式--->>用不同的作用域包含 #ifndef TIME_H #define TIME_H namespace time1 { class

http中 get方法 传送中文参数乱码解决办法

http传送参数有两种方法:get 和post  当时使用get方法传递中文参数是会有乱码现象 解决办法: 1.在jsp压面url后面的参数使用 encodeURI:"&zxbghy="+ encodeURI(zxbghy) 2.在后台java获取参数时使用字节数组:temphgdmhy = new String(hgdmhy.getBytes("ISO-8859-1"), "UTF-8");  案例,用get方法传递两个中文参数:zxb

java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: 1解决办法

java.sql.SQLException: 索引中丢失  IN 或 OUT 参数:: 1 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:125) at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:162) at oracle.jdbc.driver.OraclePreparedStatement.pr

未能从程序集“System.ServiceModel, Version=3.0.0.0”中加载类型“System.ServiceModel.Activation.HttpModule” 的解决办法

错误消息: 未能从程序集“System.ServiceModel, Version=3.0.0.0”中加载类型“System.ServiceModel.Activation.HttpModule” 的解决办法 解决办法: 1, 若要解决该问题,请在 Visual Studio 2010 命令提示符下运行下面的命令行: aspnet_regiis.exe -i -enable 如果出现以下错误的时候我们就用方法2.. PM> aspnet_regiis.exe -i -enable无法将“aspn