HTML 子元素 onMouseOver 触发父元素 onMouseOut 事件

当父级设置 onMouseOver 及 onMouseOut 时,鼠标从父级移入子级,会触发父级的 onMouseOut 后又触发 onMouseOver 事件,从子级移入父级后再次触发父级的 onMouseOut 后又触发 onMouseOver。而如果 onMouseOver 内又应用了计时器便会存在较大的问题。下面针对此问题给出解决方案。

详解 JavaScript 中或者||和并且&&运算符

首先,在给出解决方案之前,必须先弄清楚几个对象及方法,分别如下:

  1. 事件对象
  2. 事件对象相关属性(只针对onmouseover及onmouseout),即fromElement、toElement、relatedTarget
  3. 判断一个元素是否包含另一个元素的方法,即element.contains(Node)与element.compareDocumentPosition(Node)

既然前面已经说了要弄清如上几个对象及方法,那么我们就可以分析一下倒底如何去解决这个问题。

问题分析

存在的问题是设置 onmouseover 时,鼠标从移入父级内时,没任何问题,但由父级移入子级时,以及由子级称出到父级时会出现如上问题,那么我们可以想办法设置当鼠标从父级移入到子级时,或从子级移出到父级时让触发对象失效,我们可以通过 if 语句进行判断。而事件对象里面有个属性可以获取移入移出时的相关对象,下面就来介绍。

事件对象

可以获取事件对象的一系列属性,在事件中写一个参数,即可通过参数获取,方法如下:

wrap.onmouseover = function(e) {

e = window.event || e;

// window.event 是为了兼容ie下获取事件对象,而e为标准浏览器直接获取

}

事件对象的相关对象

在触发 onmouseover 及 onmouseout 时,必定会涉及到其它对象,如 onmouseover 的相关对象,即为哪个对象进入的。onmouseout 的相关对象即为进入到哪个对象。获取方法如下:

wrap.onmouseover = function(e) {

e = window.event || e;

var s = e.fromElement || e.relatedTarget;

// e.fromElement为IE下onmouseover获取相关对象方法

// relatedTarget为标准浏览器下获取方法

}

wrap.onmouseout = function(e) {

e = window.event || e;

var s = e.toElement || e.relatedTarget;

// e.toElementIE下onmouseout获取相关对象方法

// relatedTarget为标准浏览器下获取方法

}

判断元素是否有子元素

IE 下可以使用 a.contains(b) 判断 a 是否包含 b,标准浏览器下 a.compareDocumentPosition(b) 有5个值,若为 0 表示为同一节点,若为 2 表示a位于b后面,若为 4 表示a位于b前面,若为 10 表示a为b的后代,若为 20 表示a为b的祖级。

先上个即将用到的示例的 HTML 及CSS

<!doctype html>

<html>

<head>

<meta charset="UTF-8">

<style type="text/css">

html,body,div{margin:0;padding:0}

.wrapper {

overflow: hidden;

width: 800px;

background: black;

}

.box {

height: 200px;

background: #FF0;

margin: 80px 0;

}

</style>

</head>

<body>

<div class="wrapper" id="wrap">

<div class="box" id="box"></div>

</div>

</body>

</html>

既然知道了以上的获取属性及事件的必备方法,那么我们就可以想方法解决问题了。

当触发onmouseover时,可能是从对象以外移入的,也有可能是父级移入到子级,以及子级移出到父级,刚才也说过,onmouseover 的相关对象是获取从哪个对象进入的。如果是从外面的对象进入的,我们就执行所需的代码。如果是从父级移入到子级或是由子级移出到父级时,则直接跳过。

父级移入到子级对象,相关对象为父级。 子级移出到父级对象,相对对象为子级。

wrap.onmouseover = function(e) {

e = window.event || e;

var s = e.fromElement || e.relatedTarget;

if (document.all) {

//判断浏览器是否为IE,如果存在document.all则为IE

if (!this.contains(s)) {

// 判断调用onmouseover的对象(this)是否包含自身或子级,如果包含,则不执行

console.log('IE will over');

}

} else {

//标准浏览器下的方法

var reg = this.compareDocumentPosition(s);

if (!(reg == 20 || reg == 0)) {

console.log('Browser will over');

}

}

}

当触发onmouseout时,可能是从父级移到子级,也可能由子级移到父级,或是移出至父级之外,父级称到子级,则相关对象为子级,子级称到父级,则相关对象为父级。

wrap.onmouseout = function(e) {

e = window.event || e;

var s = e.toElement || e.relatedTarget;

if(document.all) {

if (!this.contains(s)) {

console.log('IE will out');

}

} else {

var reg = this.compareDocumentPosition(s);

if (!(reg == 20 || reg == 0)) {

console.log('Browser will out');

}

}

}

问题也就得到了解决。

不过你会发现:onmouseover 与 onmouseout 的判断方法其实是一样的。于是我们得到:

无论是移入还是移出,只要相关对象是父级以外的就可以执行,否则代码不执行。

兼容代码

最后,再为大家提供本人自己写的兼容代码,复制到JS代码后可直接调用。代码如下:

/* 

* 功能:鼠标移入对象触发事件,兼容所有浏览器并防止鼠标在父子对象间移动造成的触发onmouseover的bug

* 参数:第一个参数表示触发的对象,第二个参数表示触发的对象的事件对象,第三个对象表示要执行的函数

*/

function mouseover(a, e, func) {

e = e || window.event;

var b = e.fromElement || e.relatedTarget;

mouseoverAndOut(a, b, func);

}

/*

* 功能:鼠标移出对象触发事件,兼容所有浏览器并防止鼠标在父子对象间移动造成的onmouseout的bug

* 参数:第一个参数表示触发的对象,第二个参数表示触发的对象的事件对象,第三个对象表示要执行的函数

*/

function mouseout(a ,e, func) {

e = e || window.event;

var b = e.toElement || e.relatedTarget;

mouseoverAndOut(a, b, func);

}

/*

* 功能:鼠标移入或移出对象触发事件,兼容所有浏览器并防止鼠标在父子对象间移动造成的onmouseover & onmouseout的bug

* 参数:第一个参数表示触发的对象,第二个参数表示触发的对象的事件对象,第三个对象表示要执行的函数

*/

function mouseoverOrOut(a, e, func) {

e = e || window.event;

var b;

if (e.type == 'mouseover') {

b = e.fromElement || e.relatedTarget;

} else if (e.type == 'mouseout') {

b = e.toElement || e.relatedTarget;

}

mouseoverAndOut(a, e, func);

}

/*

* 功能:鼠标移入或移出对象触发事件,兼容所有浏览器并防止鼠标在父子对象间移动造成的onmouseover & onmouseout的bug

* 参数:第一个参数表示触发的对象,第二个参数表示触发的对象的相关对象,第三个对象表示要执行的函数

*/

function mouseoverAndOut(a, b, func) {

if (document.all) {

if (!(a.contains(b))) {

func();

}

} else {

var res = a.compareDocumentPosition(b);

if(!(res == 20 || res == 0)){

func();

}

}

}

如果是 onmouseover,调用 mouseover(a, e, func) 即可,如果是 onmouseout 调用 mouseout(a, e, func) 即可;或不管 onmouseover 还是 onmouseout 直接调用 mouseoverOrOut(a, e, func) 即可。

更简便的解决方法

2019年11月8日19:16:11 新加入

其实还有更简便的解决方法,鼠标从子元素到父元素,中间 onmouseout 和 onmouseover 执行的时间非常短,在界面上就会造成元素的闪动(或者是抖动),我们只需要在加一个定时器,在 onmouseout 的时候延迟处理,然后当执行 onmouseover 的时候清除定时器。

比如说一个客服组件,我们有如下的代码:

<div class="onlineBox" onmouseover="myOver" onmouseout="myOut">

<div class="onlineBoxSmall">联系我们</div>

<div class="onlineBoxBig">

....

</div>

</div>

var timeObj = null;

function myOver(){

// 鼠标移入的时候,清除定时器

// 先判断是否有定时器,防止出错

if(timeObj){

clearTimeout(timeObj);

}

$('.onlineBoxSmall').hide();

$('.onlineBoxBig').show();

}

function myOut(){

// 鼠标移出的时候,延迟200毫秒执行

timeObj = setTimeout(()=>{

$('.onlineBoxSmall').show();

$('.onlineBoxBig').hide();

}, 200)

}

由于 onmouseout 和 onmouseover 执行间隔很短,定时器还没来得及执行就被清除了,相当于就没有执行 Out 事件。

以上是 HTML 子元素 onMouseOver 触发父元素 onMouseOut 事件 的全部内容, 来源链接: utcz.com/p/232305.html

回到顶部