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 相关文章推荐
让 JavaScript 轻松支持函数重载 (Part 2 - 实现)
Aug 04 Javascript
让网页跳转到指定位置的jquery代码非书签
Sep 06 Javascript
关于JavaScript中name的意义冲突示例介绍
May 29 Javascript
js中的getAttribute方法使用示例
Aug 01 Javascript
jquery datatable后台封装数据示例代码
Aug 07 Javascript
JS中的Replace方法使用经验分享
May 20 Javascript
javascript图片预加载完整实例
Dec 10 Javascript
JavaScript拖拽、碰撞、重力及弹性运动实例分析
Jan 08 Javascript
浅谈JavaScript中数组的增删改查
Jun 20 Javascript
JS模拟实现ECMAScript5新增的数组方法
Mar 20 Javascript
DVA框架统一处理所有页面的loading状态
Aug 25 Javascript
Vue中Quill富文本编辑器的使用教程
Sep 21 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
便携利器 — TECSUN PL-365简评
2021/03/02 无线电
[FAQ]PHP中的一些常识:类篇
2006/10/09 PHP
PHP中$_SERVER的详细参数与说明
2008/07/29 PHP
兼容性最强的PHP生成缩略图的函数代码(修改版)
2011/01/18 PHP
PHP中鲜为人知的10个函数
2014/02/28 PHP
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
php判断访问IP的方法
2015/06/19 PHP
thinkPHP5 tablib标签库自定义方法详解
2017/05/10 PHP
Laravel5.1 框架控制器基础用法实例分析
2020/01/04 PHP
基于jQuery实现的水平和垂直居中的div窗口
2011/08/08 Javascript
jQuery1.6 使用方法二
2011/11/23 Javascript
ASP.NET MVC中EasyUI的datagrid跨域调用实现代码
2012/03/14 Javascript
JavaScript通过RegExp实现客户端验证处理程序
2013/05/07 Javascript
document.write()及其输出内容的样式、位置控制
2013/08/12 Javascript
JavaScript设置body高度为浏览器高度的方法
2015/02/09 Javascript
JavaScript  event对象整理及详细介绍
2016/10/10 Javascript
深入理解Angularjs中的$resource服务
2016/12/31 Javascript
react路由配置方式详解
2017/08/07 Javascript
详解小程序原生使用ES7 async/await语法
2018/08/06 Javascript
node错误处理与日志记录的实现
2018/12/24 Javascript
JQuery属性操作与循环用法示例
2019/05/15 jQuery
原理深度解析Vue的响应式更新比React快
2020/04/04 Javascript
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
vue使用require.context实现动态注册路由
2020/12/25 Vue.js
python写的ARP攻击代码实例
2014/06/04 Python
CentOS下使用yum安装python-pip失败的完美解决方法
2017/08/16 Python
基于循环神经网络(RNN)的古诗生成器
2018/03/26 Python
详解Python中的分支和循环结构
2020/02/11 Python
python的reverse函数翻转结果为None的问题
2020/05/11 Python
Python自动化办公Excel模块openpyxl原理及用法解析
2020/11/05 Python
利用Python函数实现一个万历表完整示例
2021/01/23 Python
新浪微博实习心得体会
2014/01/27 职场文书
企业职业病防治方案
2014/05/29 职场文书
超市客服工作职责
2014/06/11 职场文书
化工专业自荐书
2014/06/16 职场文书
Mysql数据库索引面试题(程序员基础技能)
2021/05/31 MySQL