在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout
Posted
技术标签:
【中文标题】在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout【英文标题】:Prevent onmouseout when hovering child element of the parent absolute div WITHOUT jQuery 【发布时间】:2011-06-09 12:36:14 【问题描述】:我在使用绝对定位 div 中的 onmouseout
函数时遇到问题。当鼠标点击 div 中的子元素时,mouseout 事件会触发,但我不希望它在鼠标离开父级绝对 div 之前触发。
如何防止mouseout
事件在没有 jquery 的情况下命中子元素时触发。
我知道这与事件冒泡有关,但我没有找到解决方法的运气。
我在这里找到了一个类似的帖子:How to disable mouseout events triggered by child elements?
但是该解决方案使用 jQuery。
【问题讨论】:
我最终使用超时解决了问题,并在子元素悬停时将其清除 嗨,我看过你的演示,但是当我浏览右下面板中的元素时,没有任何反应? 如果您希望在将鼠标悬停在子元素上时防止父事件鼠标移出,请找到此答案:javascript mouseover/mouseout。 查看@Zach Saucier answer buried below 【参考方案1】:使用onmouseleave
。
或者,在 jQuery 中,使用 mouseleave()
这正是您正在寻找的东西。示例:
<div class="outer" onmouseleave="yourFunction()">
<div class="inner">
</div>
</div>
或者,在 jQuery 中:
$(".outer").mouseleave(function()
//your code here
);
一个例子是here。
【讨论】:
onMouseLeave
也是我最终使用的,因为它不会冒泡。
为我节省了大量时间。谢谢
mouseleave
无法取消。
@grecdev,说更多!【参考方案2】:
对于在大多数情况下都有效的更简单的纯 CSS 解决方案,可以通过将子级的 pointer-events
设置为 none
来删除它们
.parent *
pointer-events: none;
浏览器支持:IE11+
【讨论】:
太棒了!完全解决了我的问题! 这应该是第一个或至少是第二个答案,我已经准备好接受事件监听器的麻烦,这也完全解决了我的问题。这么简单! 这可以防止mouseout触发,但对我来说,它也可以防止实际的孩子被点击作为菜单中的选择,这是菜单的点。 @hellofunk 这就是为什么您要将点击事件应用于父级的原因。所需的确切代码因实现而异,此答案仅建议使用pointer-events
属性
只有在区域内没有其他控制器元素(编辑、删除)时才有效,因为此解决方案也会阻止它们..【参考方案3】:
function onMouseOut(event)
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this)
return;
alert('MouseOut');
// handle mouse event here!
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
我做了一个快速的 JsFiddle 演示,包含所有需要的 CSS 和 html,请查看...
EDIT 跨浏览器支持的固定链接http://jsfiddle.net/RH3tA/9/
注意,这只检查直接父级,如果父级 div 有嵌套的子级,那么您必须以某种方式遍历父级元素以查找“原始元素”
EDIT 嵌套子级示例
编辑已修复,希望跨浏览器
function makeMouseOutFn(elem)
var list = traverseChildren(elem);
return function onMouseOut(event)
var e = event.toElement || event.relatedTarget;
if (!!~list.indexOf(e))
return;
alert('MouseOut');
// handle mouse event here!
;
//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);
//quick and dirty DFS children traversal,
function traverseChildren(elem)
var children = [];
var q = [];
q.push(elem);
while (q.length > 0)
var elem = q.pop();
children.push(elem);
pushAll(elem.children);
function pushAll(elemArray)
for(var i=0; i < elemArray.length; i++)
q.push(elemArray[i]);
return children;
还有一个新的JSFiddle,编辑更新链接
【讨论】:
您应该在小提琴中添加一个 mouseout 事件,以便对其进行测试。应该发出警报或其他事情。 抱歉,您好像有 alert('MouseOut') ,但它对我不起作用?我已经在没有 JS 库选项的情况下运行了 如果你有孙子怎么办?还是曾孙?即你能有一个递归的解决方案,而不是仅仅模仿孩子吗? 这不起作用。mouseleave
是正确答案
这是一个相当古老的答案 - 现在你可以使用 contains
这使它成为一个更优雅的解决方案:function makeMouseOutFn(event) e = event.toElement || event.relatedTarget; if (this.contains(e)) return; //handle mouse event here
【参考方案4】:
这是一个基于以下内容的更优雅的解决方案。 它解释了从多个级别的孩子冒泡的事件。 它还解决了跨浏览器问题。
function onMouseOut(this, event)
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window)
if (e.parentNode == this|| e == this)
if(e.preventDefault) e.preventDefault();
return false;
e = e.parentNode;
//Do something u need here
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
【讨论】:
Sam Elie,这似乎不适用于 IE、Safari 或 Opera。你有这个脚本的跨浏览器版本吗?它在 Firefox 和 Chrome 中就像一个魅力。谢谢。 当我将上述函数粘贴到我的脚本中时,Dreamweaver 和 FF 在第一行抛出错误,“function onMouseOut(this, event) ”。看起来您不允许将“this”作为参数。我想当我从输入参数中省略“this”时它会起作用,但我不确定,因为“我不知道我不知道什么”。 我在同一行的 Chrome 中也出现了同样的问题,除了 @GregoryLewis 提到的函数定义行中的this
解决了这个问题。此外,对于更多的跨浏览器支持,试试这个: if (window.addEventListener) document.getElementById('parent').addEventListener('mouseout',onMouseOut,true); else if (window.attachEvent) document.getElementById('parent').attachEvent("onmouseout",onMouseOut);
很帅!如果需要支持旧版浏览器的最佳答案。【参考方案5】:
使用 onmouseleave 代替 onmouseout。
您尚未向我们展示您的具体代码,因此我无法在您的具体示例中向您展示如何操作。
不过很简单:把onmouseout换成onmouseleave就行了。
就是这样 :) 所以,很简单 :)
如果不确定如何操作,请参阅以下说明:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out
平安无事 :) 尽情享受吧 :)
【讨论】:
这是一个伟大的呼喊,如果你有类似的情况,非常值得一试【参考方案6】:如果您使用 jQuery,您还可以使用“mouseleave”函数,它会为您处理所有这些。
$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);
do_something 会在鼠标进入targetdiv 或其任何子元素时触发,do_something_else 只会在鼠标离开targetdiv 及其任何子元素时触发。
【讨论】:
【参考方案7】:感谢 Amjad Masad 启发了我。
我有以下似乎适用于 IE9、FF 和 Chrome 的解决方案,并且代码很短(没有复杂的闭包和横向子项):
DIV.onmouseout=function(e)
// check and loop relatedTarget.parentNode
// ignore event triggered mouse overing any child element or leaving itself
var obj=e.relatedTarget;
while(obj!=null)
if(obj==this)
return;
obj=obj.parentNode;
// now perform the actual action you want to do only when mouse is leaving the DIV
【讨论】:
【参考方案8】:我认为Quirksmode 有你需要的所有答案(不同的浏览器冒泡行为和 mouseenter/mouseleave 事件),但我认为该事件冒泡混乱的最常见结论是 使用 JQuery 或 Mootools 之类的框架(具有 mouseenter 和 mouseleave 事件,这正是您的直觉)。
看看他们是怎么做的,如果你愿意,可以自己做或你可以create your custom Mootools 的“精益平均”版本,只有事件部分(及其依赖项)。
【讨论】:
【参考方案9】:试试mouseleave()
例子:
<div id="parent" mouseleave="function">
<div id="child">
</div>
</div>
;)
【讨论】:
【参考方案10】:我找到了一个非常简单的解决方案,
只使用 onmouseleave="myfunc()" 事件而不是 onmousout="myfunc()" 事件
在我的代码中它起作用了!!
示例:
<html>
<head>
<script type="text/javascript">
function myFunc()
document.getElementById('hide_div').style.display = 'none';
function ShowFunc()
document.getElementById('hide_div').style.display = 'block';
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It doesn't fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div" >TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>
鼠标移出功能的相同示例:
<html>
<head>
<script type="text/javascript">
function myFunc()
document.getElementById('hide_div').style.display = 'none';
function ShowFunc()
document.getElementById('hide_div').style.display = 'block';
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div">TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>
希望对你有帮助:)
【讨论】:
【参考方案11】:虽然你提到的solution使用jquery, mouseenter 和 mouseleave 是原生 dom 事件,因此您可以不使用 jquery。
【讨论】:
【参考方案12】:有两种方法可以解决这个问题。
1) 检查回调中的 event.target 结果,看看它是否与你的父 div 匹配
var g_ParentDiv;
function OnMouseOut(event)
if (event.target != g_ParentDiv)
return;
// handle mouse event here!
;
window.onload = function()
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.onmouseout = OnMouseOut;
;
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
2) 或者使用事件捕获并在回调函数中调用 event.stopPropagation
var g_ParentDiv;
function OnMouseOut(event)
event.stopPropagation(); // don't let the event recurse into children
// handle mouse event here!
;
window.onload = function()
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
;
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
【讨论】:
如何让 onmouseout 而不是 onmouseover 工作? 糟糕。我的错。我只是在上面的代码中将所有“mouseover”引用更改为“mouseout”。那应该为你做。 第一种方法,当鼠标移到子元素上时,event.target总是匹配父元素,所以不起作用 第二种方法也会发生同样的事情。当鼠标进入子元素时仍然触发事件【参考方案13】:我让它像一个魅力一样工作:
function HideLayer(theEvent)
var MyDiv=document.getElementById('MyDiv');
if(MyDiv==(!theEvent?window.event:theEvent.target))
MyDiv.style.display='none';
啊,MyDiv
标签是这样的:
<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
这样,当 onmouseout 转到孩子、孙子等时,style.display='none'
不会被执行;但是当 onmouseout 退出 MyDiv 时它会运行。
因此无需停止传播、使用计时器等...
感谢您提供的示例,我可以从中制作此代码。
希望这对某人有所帮助。
也可以这样改进:
function HideLayer(theLayer,theEvent)
if(theLayer==(!theEvent?window.event:theEvent.target))
theLayer.style.display='none';
然后是这样的DIVs标签:
<div onmouseout="JavaScript: HideLayer(this,event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
所以更通用,不仅是一个div,而且不需要在每一层上添加id="..."
。
【讨论】:
【参考方案14】:如果您可以在mouseout
方法中访问事件附加到的元素,则可以使用contains()
来查看event.relatedTarget
是否为子元素。
由于event.relatedTarget
是鼠标传入的元素,如果不是子元素,则表示鼠标移出该元素。
div.onmouseout = function (event)
if (!div.contains(event.relatedTarget))
// moused out of div
【讨论】:
【参考方案15】:在 Angular 5、6 和 7 上
<div (mouseout)="onMouseOut($event)"
(mouseenter)="onMouseEnter($event)"></div>
然后
import Component,Renderer2 from '@angular/core';
...
@Component(
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
)
export class TestComponent implements OnInit, OnDestroy
...
public targetElement: HTMLElement;
constructor(private _renderer: Renderer2)
ngOnInit(): void
ngOnDestroy(): void
//Maybe reset the targetElement
public onMouseEnter(event): void
this.targetElement = event.target || event.srcElement;
console.log('Mouse Enter', this.targetElement);
public onMouseOut(event): void
const elementRelated = event.toElement || event.relatedTarget;
if (this.targetElement.contains(elementRelated))
return;
console.log('Mouse Out');
【讨论】:
【参考方案16】:我们可以简单地检查 e.relatedTarget 是否有子类,如果为真则返回函数。
if ($(e.relatedTarget).hasClass("ctrl-btn"))
return;
这是为我工作的代码,我用于 html5 视频播放,暂停按钮切换悬停视频元素
element.on("mouseover mouseout", function(e)
if(e.type === "mouseout")
if ($(e.relatedTarget).hasClass("child-class"))
return;
);
【讨论】:
【参考方案17】:我检查原始元素的偏移量以获取元素边界的页面坐标,然后确保仅在 mouseout 超出这些边界时触发 mouseout 操作。很脏,但它可以工作。
$(el).live('mouseout', function(event)
while(checkPosition(this, event))
console.log("mouseovering including children")
console.log("moused out of the whole")
)
var checkPosition = function(el, event)
var position = $(el).offset()
var height = $(el).height()
var width = $(el).width()
if (event.pageY > position.top
|| event.pageY < (position.top + height)
|| event.pageX > position.left
|| event.pageX < (position.left + width))
return true
【讨论】:
【参考方案18】:var elem = $('#some-id');
elem.mouseover(function ()
// Some code here
).mouseout(function (event)
var e = event.toElement || event.relatedTarget;
if (elem.has(e).length > 0) return;
// Some code here
);
【讨论】:
请用你的回答者给出一些解释。【参考方案19】:如果您向父元素添加(或拥有)一个 CSS 类或 id,那么您可以执行以下操作:
<div id="parent">
<div>
</div>
</div>
JavaScript:
document.getElementById("parent").onmouseout = function(e)
e = e ? e : window.event //For IE
if(e.target.id == "parent")
//Do your stuff
所以只有当事件在父 div 上时才会执行。
【讨论】:
【参考方案20】:我只是想和你分享一些东西。
我在ng-mouseenter
和ng-mouseleave
事件中遇到了一些困难。
案例研究:
我创建了一个浮动导航菜单,当光标悬停在图标上时它会切换。 此菜单位于每一页的顶部。
为了处理菜单上的显示/隐藏,我切换了一个类。ng-class="down: vm.isHover"
要切换 vm.isHover,我使用 ng 鼠标事件。ng-mouseenter="vm.isHover = true"
ng-mouseleave="vm.isHover = false"
目前,一切都很好,并且按预期工作。 解决方案干净简单。
传入问题:
在特定视图中,我有一个元素列表。 当光标位于列表的某个元素上时,我添加了一个操作面板。 我使用与上面相同的代码来处理该行为。
问题:
我发现当我的光标在浮动导航菜单上以及元素顶部时,彼此之间存在冲突。 操作面板出现了,浮动导航被隐藏了。
问题是即使光标在浮动导航菜单上,也会触发列表元素 ng-mouseenter。 这对我来说毫无意义,因为我希望鼠标传播事件会自动中断。 我必须说我很失望,我花了一些时间来找出这个问题。
初步想法:
我尝试使用这些:
$event.stopPropagation()
$event.stopImmediatePropagation()
我结合了很多 ng 指针事件(mousemove、mouveover、...),但没有一个能帮到我。
CSS 解决方案:
我找到了一个简单的 css 属性的解决方案,我越来越多地使用它:
pointer-events: none;
基本上,我就是这样使用它的(在我的列表元素上):
ng-style="'pointer-events': vm.isHover ? 'none' : ''"
有了这个棘手的问题,ng-mouse 事件将不再被触发,当光标在它和列表中的一个元素上时,我的浮动导航菜单将不再关闭自己。
更进一步:
如您所料,此解决方案有效,但我不喜欢它。
我们无法控制自己的事件,这很糟糕。
另外,您必须有权访问 vm.isHover
范围才能实现这一目标,这可能是不可能的,但在某种程度上是肮脏的。
如果有人想看,我可以做一个小提琴。
不过,我没有其他解决方案...
说来话长,我不能给你一个土豆,所以请原谅我的朋友。
无论如何,pointer-events: none
就是生命,所以记住它。
【讨论】:
【参考方案21】:有一种简单的方法可以让它工作。你设置的元素和所有的子类名一样,那么:
element.onmouseover = function(event)
if (event.target.className == "name")
/*code*/
【讨论】:
【参考方案22】:对于 vanillajs,您也可以使用这种方式
document.querySelector('.product_items') && document.querySelector('.product_items').addEventListener('mouseleave', () => updateCart())
const updateCart = () =>
let total = 0;
document.querySelectorAll('input') && document.querySelectorAll('input').forEach(item => total += +item.value)
document.getElementById('total').innerHTML = total
<div class="product_items">
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
</div>
<div id="total"></div>
【讨论】:
以上是关于在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout的主要内容,如果未能解决你的问题,请参考以下文章