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

问题描述

我希望当鼠标移动到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)返回的信息描述如下:

比特 序号 意义

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

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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索mouseover
, mouseout
mouseover多次触发
mouseover 多次触发、mousewheel被多次触发、mousewheel 触发多次、mouseover mouseout、mouseover mouseenter,以便于您获取更多的相关知识。

时间: 2024-11-02 13:18:24

JS中mouseover和mouseout多次触发问题如何解决_javascript技巧的相关文章

js中的事件捕捉模型与冒泡模型实例分析_javascript技巧

本文实例讲述了js中的事件捕捉模型与冒泡模型.分享给大家供大家参考. 具体实现方法如下: 实例1: 复制代码 代码如下: <html> <head> <script type="text/javascript">  window.onload = function(){   document.getElementById('par').addEventListener('click',function() {alert('par');},true);

关于JS中match() 和 exec() 返回值和属性的测试_javascript技巧

语法: exec() : RegExpObject.exec(string) match() : stringObject.match(string) stringObject.match(regexp) 知识点: exec() 是RegExp对象的方法,而 match() 是String对象的方法. 都会返回包含第一个匹配项信息的数组:或者在没有匹配项的情况下返回null. 返回的数组虽然是Array 的实例,但包含两个额外的属性:index 和 input.其中,index 表示匹配项在字符

JS中动态创建元素的三种方法总结(推荐)_javascript技巧

1.动态创建元素一 document.write() 例如向页面中输出一个 li 标签 <pre class="html" name="code"><span style="font-size:12px;"><script> document.write("<li>123</li>"); </script></span> body标签中就会插入

JS中检测数据类型的几种方式及优缺点小结_javascript技巧

1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string"."boolean"."undefined"."object"."function" 局限性: 1)typeof null ->"object" 2)检测的不管是数组还是正则都返回的是"ob

教你JS中的运算符乘方、开方及变量格式转换_javascript技巧

1)如何计算乘方 题一:3的4次方(不会打,请原谅 ==!!!) 3的4次方=3*3*3*3 var a = Math.pow(3,4); console.log(a); 说明:Math.pow()是用来计算乘方的语法 注意:Math的M是大写: 题二:3的4*5次方 var a =Math.pow(3,4*5); console.log(a); 2)如何计算根号 题目:根号81 var a = Math.sqrt(81); console.log(a); 变量格式转换 用户的输入 var ye

JS中Iframe之间传值及子页面与父页面应用_javascript技巧

在js中,我们时常用到用iframe做系统框架,在子页面也,父页面之间的值传递是一个问题,下面是js获取父窗体和子窗体的对象js: 1.在iframe子页面中获取父页面的元素: a>window.parent.document这个是获取父页面document中的对象: b>如果要获取父页面js中的方法:window.parent.xxxx():xxxx()为方法: 2.在父页面中获取iframe子页面中的元素: a> 复制代码 代码如下: var child = document.get

获取JS中网页各种高宽与位置的方法总结_javascript技巧

screen对象 获取屏幕的高宽(分辨率) screen.width //屏幕的宽 screen.height //屏幕的高 screen.availWidth //屏幕可用宽度 屏幕的像素高度减去系统部件高度之后的值 screen.availHeight //屏幕可用高度 屏幕的像素宽度减去系统部件宽度之后的值 window对象 获得窗口位置及大小 window.screenTop //窗口顶部距屏幕顶部的距离 window.screenLeft //窗口左侧距屏幕左侧的距离 window.i

js中eval()函数和trim()去掉字符串左右空格应用_javascript技巧

对于js中eval()函数的理解和写一个函数trim()去掉字符串左右空格. trim()是参照了jquery的源码,你可以放心使用. 对于js中eval()函数的理解是本人心得不一定正确. 复制代码 代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <he

JS中自定义定时器让它在某一时刻执行_javascript技巧

有时,处于需求的原因,我们需要在JS中写一个方法,然后让它在某一时刻执行,即需要在JS中写一个定时器,当时间达到要求时间时,需要执行的方法自动执行,下面小编简单说一下我是怎么实现的 var tMinutes=0; var tHours=0; var go; function dingshi(hours,minutes){ tHours = hours; tMinutes = minutes; go=setInterval(run,3000); } function run(){ var date