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


Posted in Javascript onJune 06, 2016

问题描述

我希望当鼠标移动到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>

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

问题解决办法

最开始的问题分析,当鼠标从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上,控制台输出如下图

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

可以看出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函数如下

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

可以简化为如下

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

比特 序号 意义

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

通过上面我们就可以理解为什么要写成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效果效果。

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

代码的封装

如果每次进行这样的操作,都需要加载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>

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

Javascript 相关文章推荐
jQuery 学习第五课 Ajax 使用说明
May 17 Javascript
js显示当前日期时间和星期几
Oct 22 Javascript
JavaScript实现多种排序算法
Feb 24 Javascript
jQuery实现两个select控件的互移操作
Dec 22 Javascript
canvas绘制七巧板
Feb 03 Javascript
基于javascript的异步编程实例详解
Apr 10 Javascript
AngularJS select设置默认值的实现方法
Aug 25 Javascript
详解Vue项目中实现锚点定位
Apr 24 Javascript
深入解读Node.js中的koa源码
Jun 17 Javascript
mpvue 项目初始化及实现授权登录的实现方法
Jul 20 Javascript
如何在 ant 的table中实现图片的渲染操作
Oct 28 Javascript
原生js实现放大镜组件
Jan 22 Javascript
js获取元素的外链样式的简单实现方法
Jun 06 #Javascript
node.js cookie-parser之parser.js
Jun 06 #Javascript
gulp-htmlmin压缩html的gulp插件实例代码
Jun 06 #Javascript
node.js cookie-parser 中间件介绍
Jun 06 #Javascript
JavaScript 对象字面量讲解
Jun 06 #Javascript
省市联动效果的简单实现代码(推荐)
Jun 06 #Javascript
js获取新浪天气接口的实现代码
Jun 06 #Javascript
You might like
MySQL相关说明
2007/01/15 PHP
解决php中Cannot send session cache limiter 的问题的方法
2007/04/27 PHP
php面向对象全攻略 (五) 封装性
2009/09/30 PHP
zend api扩展的php对象的autoload工具
2011/04/18 PHP
PHP管理内存函数 memory_get_usage()使用介绍
2012/09/23 PHP
解析zend studio中直接导入svn中的项目的方法步骤
2013/06/21 PHP
PHP日期函数date格式化UNIX时间的方法
2015/03/19 PHP
jQuery TextBox自动完成条
2009/07/22 Javascript
js 判断上传文件大小及格式代码
2013/11/13 Javascript
JS Loading功能的简单实现
2013/11/29 Javascript
jQuery实现加入购物车飞入动画效果
2015/03/14 Javascript
全系IE支持Bootstrap的解决方法
2015/10/19 Javascript
JS实现的论坛Ajax打分效果完整实例
2015/10/31 Javascript
炫酷的js手风琴效果
2016/10/13 Javascript
js将字符串中的每一个单词的首字母变为大写其余均为小写
2017/01/05 Javascript
利用JS实现简单的日期选择插件
2017/01/23 Javascript
Vuex 入门教程
2018/01/10 Javascript
微信小程序bindinput与bindsubmit的区别实例分析
2019/04/17 Javascript
js实现html滑动图片拼图验证
2020/06/24 Javascript
html5以及jQuery实现本地图片上传前的预览代码实例讲解
2021/03/01 jQuery
Python3遍历目录树实现方法
2015/05/22 Python
Django 实现下载文件功能的示例
2018/03/06 Python
Python异常的检测和处理方法
2018/10/26 Python
Python Tkinter 简单登录界面的实现
2019/06/14 Python
关于Keras Dense层整理
2020/05/21 Python
python 基于wx实现音乐播放
2020/11/24 Python
中海讯通笔试题
2015/09/15 面试题
七一党建活动方案
2014/01/28 职场文书
《大海那边》教学反思
2014/04/09 职场文书
单位授权委托书范文
2014/08/02 职场文书
公司优秀员工获奖感言
2014/08/14 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
公安四风对照检查材料思想汇报
2014/10/11 职场文书
四风问题自查自纠工作情况报告
2014/10/28 职场文书
班主任寄语2016
2015/12/04 职场文书
golang slice元素去重操作
2021/04/30 Golang