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获取文本框中字符长度的代码
Sep 29 Javascript
jQuery 取值、赋值的基本方法整理
Mar 31 Javascript
JS实现带鼠标效果的头像及文章列表代码
Sep 27 Javascript
FullCalendar日历插件应用之数据展现(一)
Dec 23 Javascript
jQuery实现的鼠标滑过弹出放大图片特效
Jan 08 Javascript
Angular.Js的自动化测试详解
Dec 09 Javascript
基于Vue2.0的分页组件
Mar 16 Javascript
基于javaScript的this指向总结
Jul 22 Javascript
ES6中Array.find()和findIndex()函数的用法详解
Sep 16 Javascript
Vue v2.5 调整和更新不完全问题
Oct 24 Javascript
jQuery实现的别踩白块小游戏完整示例
Jan 07 jQuery
Vue3.0中Ref与Reactive的区别示例详析
Jul 07 Vue.js
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
php注入实例
2006/10/09 PHP
Php注入点构造代码
2008/06/14 PHP
phpMyAdmin 链接表的附加功能尚未激活的问题
2010/08/01 PHP
ThinkPHP模板判断输出Defined标签用法详解
2014/06/30 PHP
PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法
2016/02/15 PHP
PHP数组实际占用内存大小原理解析
2020/12/11 PHP
php并发加锁问题分析与设计代码实例讲解
2021/02/26 PHP
JavaScript的目的分析
2007/01/05 Javascript
用JavaScript实现单继承和多继承的简单方法
2009/03/29 Javascript
使用jQuery动态加载js脚本文件的方法
2014/04/03 Javascript
jQuery中hide()方法用法实例
2014/12/24 Javascript
JavaScript使用setTimeout实现延迟弹出警告框的方法
2015/04/07 Javascript
AngularJs Understanding the Controller Component
2016/09/02 Javascript
jQuery实现标签页效果实战(4)
2017/02/08 Javascript
SVG动画vivus.js库使用小结(实例代码)
2017/09/14 Javascript
jQuery.Sumoselect插件实现下拉复选框效果
2017/11/09 jQuery
JS实现遍历不规则多维数组的方法
2018/03/21 Javascript
全站最详细的Vuex教程
2018/04/13 Javascript
JS实现监控微信小程序的原理
2018/06/15 Javascript
JavaScript实现shuffle数组洗牌操作示例
2019/01/03 Javascript
JavaScript碎片—函数闭包(模拟面向对象)
2019/03/13 Javascript
微信小程序中插入激励视频广告并获取收益(实例代码)
2019/12/06 Javascript
js实现点击按钮随机生成背景颜色
2020/09/05 Javascript
[54:54]Newbee vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
详解python函数传参是传值还是传引用
2018/01/16 Python
Python 比较文本相似性的方法(difflib,Levenshtein)
2018/10/15 Python
Python删除n行后的其他行方法
2019/01/28 Python
python自动化工具之pywinauto实例详解
2019/08/26 Python
python实现按首字母分类查找功能
2019/10/31 Python
英国巧克力贸易公司:Chocolate Trading Company
2017/03/21 全球购物
Zooplus罗马尼亚:宠物食品和配件
2019/11/02 全球购物
星级党支部申报材料
2014/05/31 职场文书
公务员群众路线心得体会
2014/11/03 职场文书
2014年基层党支部工作总结
2014/12/04 职场文书
乐山大佛导游词
2015/02/02 职场文书
导游词创作书写原则以及开场白技巧怎么学?
2019/09/25 职场文书