我在绝对定位div中遇到onmouseout
函数时遇到问题。当鼠标命中div中的子元素时,mouseout事件会触发,但我不想在鼠标超出父绝对div之前触发它。
如何阻止mouseout
事件在遇到没有jquery的子元素时触发。
我知道这与事件冒泡有关,但我没有找到如何解决这个问题的运气。
我在这里找到了类似的帖子:How to disable mouseout events triggered by child elements?
但是该解决方案使用jQuery。
答案 0 :(得分:88)
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 FIXED链接http://jsfiddle.net/RH3tA/9/
注意这只检查直接父级,如果父级div有嵌套子级,那么你必须以某种方式遍历父级寻找“原始元素”
嵌套子项的编辑示例
编辑已修复希望跨浏览器
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,编辑更新了链接
答案 1 :(得分:88)
使用onmouseleave
。
或者,在jQuery中,使用mouseleave()
这正是你要找的东西。例如:
<div class="outer" onmouseleave="yourFunction()">
<div class="inner">
</div>
</div>
或者,在jQuery中:
$(".outer").mouseleave(function(){
//your code here
});
一个例子是here。
答案 2 :(得分:82)
对于在某些情况下(IE11 or newer)有效的简单纯CSS解决方案,可以通过将其设置为none
来删除子pointer-events
.parent * {
pointer-events: none;
}
答案 3 :(得分:27)
基于以下内容,这是一个更优雅的解决方案。 它解释了从多个级别的孩子中冒出来的事件。 它还解决了跨浏览器问题。
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);
答案 4 :(得分:12)
感谢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
}
答案 5 :(得分:12)
如果你正在使用jQuery,你也可以使用&#34; mouseleave&#34;功能,为您处理所有这些。
$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);
当鼠标进入thetargetdiv或其任何子节点时,do_something将触发,do_something_else只会在鼠标离开thetargetdiv及其任何子节点时触发。
答案 6 :(得分:7)
我认为Quirksmode拥有您需要的所有答案(不同的浏览器冒泡行为和 mouseenter / mouseleave 事件),但我认为该事件最常见的结论是冒泡混乱 使用像JQuery或Mootools这样的框架(它具有 mouseenter 和 mouseleave 事件,这正是你直觉会发生的事情)。
看看他们是如何做的,如果你愿意,可以自己动手 或你只需要事件部分(及其依赖项)就可以create your custom“精益平均”版本的Mootools。
答案 7 :(得分:7)
尝试mouseleave()
示例:
<div id="parent" mouseleave="function">
<div id="child">
</div>
</div>
)
答案 8 :(得分:5)
我找到了一个非常简单的解决方案,
只需使用onmouseleave =&#34; myfunc()&#34;事件比onmousout =&#34; myfunc()&#34;事件
在我的代码中它有效!!
示例:强>
<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>
使用mouseout功能的相同示例:
<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>
希望有所帮助:)
答案 9 :(得分:1)
有两种方法可以解决这个问题。
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>
答案 10 :(得分:1)
在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');
}
}
答案 11 :(得分:1)
虽然你提到的solution使用了jquery, mouseenter和mouseleave是本机dom事件,因此您可以在没有jquery的情况下使用。
答案 12 :(得分:1)
如果您有权访问mouseout
方法中附加事件的元素,则可以使用contains()
查看event.relatedTarget
是否为子元素。
由于event.relatedTarget
是鼠标所传入的元素,如果它不是子元素,则您已将其从元素中删除。
div.onmouseout = function (event) {
if (!div.contains(event.relatedTarget)) {
// moused out of div
}
}
答案 13 :(得分:1)
我用它来做它的魅力:
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';
}
}
然后DIV标签如下:
<div onmouseout="JavaScript: HideLayer(this,event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
更一般,不仅是一个div而且不需要在每一层添加id="..."
。
答案 14 :(得分:0)
如果您向父元素添加(或拥有)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上时才会执行东西。
答案 15 :(得分:0)
我只是想和你分享一些东西
我在ng-mouseenter
和ng-mouseleave
事件中遇到了一些困难。
案例研究:
我创建了一个浮动导航菜单,当光标位于图标上时,该菜单会切换 此菜单位于每个页面的顶部。
ng-class="{down: vm.isHover}"
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
就是生命,所以记住它。
答案 16 :(得分:0)
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
});
答案 17 :(得分:0)
我检查原始元素的偏移量以获取元素边界的页面坐标,然后确保仅当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 :(得分:0)
使用onmouseleave代替onmouseout。
您尚未向我们展示您的特定代码,因此我无法在您的特定示例中向您展示如何执行此操作。
但这很简单:只需将onmouseout替换为onmouseleave。
仅此而已:)所以,很简单:)
如果不确定如何做,请参见以下说明:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out
蛋糕的和平:)享受它:)
答案 19 :(得分:0)
我们可以简单地检查e.relatedTarget是否具有子类,如果为true,则返回该函数。
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;
}
}
});
答案 20 :(得分:0)
有一种简单的方法可以让它发挥作用。你设置的元素和所有子元素都设置了相同的类名,然后:
element.onmouseover = function(event){
if (event.target.className == "name"){
/*code*/
}
}
答案 21 :(得分:0)
对于 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>